Arbre intervallaire, gestion, déplacement de sous-arbres.

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Arbre intervallaire, gestion, déplacement de sous-arbres.

Re: Arbre intervallaire, gestion, déplacement de sous-arbres

par niuxe » 28 févr. 2013, 01:47

Bonsoir Jojolapine,

Je te remercie pour cette petite classe. Je vais regarder cela de plus près ce WE. J'adapterai cette dernière à mon framework made by me.

Bon dev et merci encore:)

Re: Arbre intervallaire, gestion, déplacement de sous-arbres

par jojolapine » 17 févr. 2013, 20:59

Bonsoir,

Voici à quoi ressemble ma classe utilitaire pour manipuler mes arbres intervallaires.
Il faut bien noter qu'elle n'a pas été créée pour être réutilisée n'importe où, il y a quelques dépendances à l'environnement dans lequel je l'utilise actuellement :
  • L'utilisation de Zend_db pour ce qui est de l'interfaçage BDD
  • Utilisation d'un arbre intervallaire « nivellé » (c'est à dire avec une colonne « level » indiquant la profondeur de l'élément)
  • Globalement tout ce qui a été implémenté est issu de ce cours : http://sqlpro.developpez.com/cours/arborescence (donc insertion par la gauche, etc...)
Et pour finir je n'ai bien sûr implémenté que les méthodes dont j'ai eu besoin jusqu'à présent, je mettrais à jour si cette classe évolue par la suite (et si j'y pense).

/!\ Cette classe n'a pas été testée sous toutes les coutures et présente certainement des défauts de conception, je ne saurais être tenu responsable en cas de dysfonctionnement ;)
<?php


/**
 * Classe de manipulation de d'arbres intervallaires
 * 
 * @author Joris Mulliez
 * @package ArbreIntervallaire
 */
class Application_Model_ArbreIntervallaire {
    
    // nom des colonnes pour les bornes gauche et droite
    static $_bg = 'borne_gauche';
    static $_bd = 'borne_droite';
    
    /**
     * Insertion d'un élément
     * 
     * @param string $table Nom de la table représentant l'arbre
     * @param int $borne_gauche_parent  Borne gauche du parent
     * @param array $data   Données supplémentaires à insérer de la forme array(clé=>valeur,...)
     * @return mixed    Retourne l'id de l'élément inséré ou false en cas d'échec
     */
    public static function insert($table,$borne_gauche_parent,Array $data=array()){
    
        $db = Zend_Db_Table_Abstract::getDefaultAdapter();
        
        // préparation des données
        $fields=array(self::$_bg,self::$_bd);
        $values = array($borne_gauche_parent+1,$borne_gauche_parent+2);
        
        // préparation des données supplémentaires
        foreach($data as $k=>$v){
            $values[]=$v;
            $fields[]=$k;
        }
        
        $interros = array_fill(0,count($values),'?');
        
        // toute manipulation d'écriture sur arbre intervallaire nécessite
        // l'utilisation des transactions (car multiples requêtes)
        $db->beginTransaction();

        try {
            
            // Mise à jour des bornes des éléments éxistants
            $db->query('UPDATE '.$table.' SET '.self::$_bd.' = '.self::$_bd.' + 2 WHERE '.self::$_bd.' > ?',$borne_gauche_parent);
            $db->query('UPDATE '.$table.' SET '.self::$_bg.' = '.self::$_bg.' + 2 WHERE '.self::$_bg.' > ?',$borne_gauche_parent);
            // insertion du nouvel élément
            $db->query('INSERT INTO '.$table.' ('.implode(',',$fields).') VALUES ('.implode(',',$interros).')',$values);
            
            $db->commit();
            
            // récupération de l'id de l'élément inséré
            $return = $db->fetchRow($db->select()->from($table,'MAX(id_'.substr($table,0,-1).') as lastId'));
            $return = $return['lastId'];
            
        } catch (Exception $e) {
            
            $db->rollBack();
            $return = false;
        }
        
        return $return;
    }
    
    /**
     * Suppression d'un élément et de ses enfants (s'il en a)
     * 
     * @param string $table Nom de la table représentant l'arbre
     * @param int $borne_gauche Borne gauche de l'élément
     * @param int $borne_droite Borne droite de l'élément
     * @return boolean
     */
    public static function deleteChildren($table,$borne_gauche,$borne_droite){
        
        $db = Zend_Db_Table_Abstract::getDefaultAdapter();
        $db->beginTransaction();
        
        try {
            
            $db->query('DELETE FROM '.$table.' WHERE '.self::$_bg.' >= ? AND '.self::$_bd.' <= ?',array($borne_gauche,$borne_droite));
            $db->query('UPDATE '.$table.' SET '.self::$_bd.' = '.self::$_bd.' - '.($borne_droite-$borne_gauche+1).' WHERE '.self::$_bd.' >= ?',$borne_gauche);
            $db->query('UPDATE '.$table.' SET '.self::$_bg.' = '.self::$_bg.' - '.($borne_droite-$borne_gauche+1).' WHERE '.self::$_bg.' >= ?',$borne_gauche);

            $db->commit();
            $return = true;

        } catch (Exception $e) {
            
            $db->rollBack();
            $return = false;
        }
        
        return $return;
    }
    
    /**
     * Récupère tout les enfants d'un éléments (feuilles ou noeuds)
     * 
     * @param string $table Nom de la table représentant l'arbre
     * @param int $borne_gauche Borne gauche de l'élément parent
     * @param int $borne_droite Borne droite de l'élément parent
     * @param boolean $exclude_parent permet d'inclure ou non l'élément parent
     * @return mixed résultats de la requête ou false en cas d'échec
     */
    public static function getChildren($table,$borne_gauche,$borne_droite,$exclude_parent=false){
        
        $db = Zend_Db_Table_Abstract::getDefaultAdapter();
        
        try {
            
            $return = $db->fetchAll($db->select()->from($table)
                    ->where(self::$_bg.' >'.($exclude_parent ? '':'=').' ?',$borne_gauche)
                    ->where(self::$_bd.' <'.($exclude_parent ? '':'=').' ?',$borne_droite)
                    ->where('level > 0')
            );

        } catch (Exception $e) {
            
            $return = false;
        }
        
        return $return;
    }
    
    
    /**
     * Compte tout les enfants d'un éléments (feuilles ou noeuds)
     * 
     * @param string $table Nom de la table représentant l'arbre
     * @param int $borne_gauche Borne gauche de l'élément parent
     * @param int $borne_droite Borne droite de l'élément parent
     * @param boolean $exclude_parent permet d'inclure ou non l'élément parent
     * @return mixed résultats de la requête ou false en cas d'échec
     */
    public static function countChildren($table,$borne_gauche,$borne_droite,$exclude_parent=true){
        
        $db = Zend_Db_Table_Abstract::getDefaultAdapter();
        
        try {
            
            $return = $db->fetchRow($db->select()->from($table,'count(*) as nb')
                    ->where(self::$_bg.' >'.($exclude_parent ? '':'=').' ?',$borne_gauche)
                    ->where(self::$_bd.' <'.($exclude_parent ? '':'=').' ?',$borne_droite)
                    ->where('level > 0')
            );
            
            $return = $return['nb'];

        } catch (Exception $e) {
            
            $return = false;
        }
        
        return $return;
    }
    
    /**
     * Récupère tout les parents de l'élément (du plus proche au plus éloigné)
     * 
     * @param string $table Nom de la table représentant l'arbre
     * @param int $borne_gauche Borne gauche de l'élément
     * @param int $borne_droite Borne droite de l'élément
     * @param array $additional_where conditions additionelles
     * @return mixed ensemble de résultats ou false en cas d'échec
     */
    public static function getParents($table, $borne_gauche, $borne_droite, Array $additional_where=array()){
        
        $db = Zend_Db_Table_Abstract::getDefaultAdapter();
        
        try {
            
            $select = $db->select()->from($table)
                    ->where(self::$_bg.' < ?',$borne_gauche)
                    ->where(self::$_bd.' > ?',$borne_droite)
                    ->where('level > 0')
                    ->order('level DESC');
            
            foreach($additional_where as $w){
                if(is_array($w))
                    $select->where($w[0],$w[1]);
                else
                    $select->where($w);
            }
            
            $return = $db->fetchAll($select);

        } catch (Exception $e) {
            
            $return = false;
        }
        
        return $return;

    }
    
    /**
     * Récupère le premier parent de l'élément
     *  
     * @param string $table Nom de la table représentant l'arbre
     * @param int $borne_gauche Borne gauche de l'élément
     * @return boolean
     */
    public static function getParent($table,$borne_gauche){
        
        $db = Zend_Db_Table_Abstract::getDefaultAdapter();
        
        try {
            
            $return = $db->fetchRow(
                $db->select()->from($table)
                        ->where(self::$_bg.' <= ?',$borne_gauche)
                        ->where(self::$_bd.' - '.self::$_bg.' > 1')
                        ->order('('.self::$_bd.' - '.self::$_bg.') ASC')
            );

        } catch (Exception $e) {
            
            $return = false;
        }
        
        return $return;

    }
}
Bon amusement !

Re: Arbre intervallaire, gestion, déplacement de sous-arbres

par niuxe » 17 févr. 2013, 15:45

J'essayerais peut-être de filer ma classe de méthodes toutes prêtes pour arbre intervallaire
+1 En ce moment, je me fabrique un MVC. Je serai curieux de lire ton code et de l'adapter à mon MVC :)

Re: Arbre intervallaire, gestion, déplacement de sous-arbres

par jojolapine » 16 févr. 2013, 10:57

Effectivement c'est la dessus que je suis partit.
Et ma fois pour l'instant ça fonctionne plutôt bien !
Mais je suis pas encore rendu :)

J'essayerais peut-être de filer ma classe de méthodes toutes prêtes pour arbre intervallaire

Re: Arbre intervallaire, gestion, déplacement de sous-arbres

par niuxe » 16 févr. 2013, 00:36

J'ai commencé à essayer de déplacer les intrus d'un arbre vers son arbre, mais je ne crois pas pouvoir y arriver... :/ ou au pris de fortes migraines...
+1 pour la migraine. (Tu veux un Doliprane ?) D'ailleurs c'est prévisible.
Je me demande donc si je vais pas créer autant d'arbres que d'utilisateurs...
+1. Pour moi ça fait partie de la config du user et non pas la configuration générale. Sauf dans le cas où ton application soit modifiable par tous les users. Dans ce cas là, c'est un seul arbre visible par tout le monde dans sa totalité.

Arbre intervallaire, gestion, déplacement de sous-arbres.

par jojolapine » 25 janv. 2013, 21:51

Bonsoir à tous,

J'aurais besoin de vos lumières.
J'ai mis en place un système d'arbre intervallaire pour gérer des « actions » dans une application (grâce à ce bon tuto qui m'a bien aidé : http://sqlpro.developpez.com/cours/arborescence/ ).

J'ai donc désormais un seul arbre intervallaire qui gère toutes les actions.
Seulement ses actions appartiennent à des utilisateurs.
Donc chaque utilisateurs peut modifier l'organisation de son arbre uniquement sur ses propres actions.

J'utilise ce plugin js pour faire les modifications visuelles : https://github.com/mjsarfatti/nestedSortable
Ce dernier me renvoi tout seul une représentation intervallaire, donc c'est tout bon pour moi !
Sauf ... si les arbres des utilisateurs se croisent... un utilisateur rajoute une action à son arbre après un autre.
Et paf les bornes ne sont plus contigues en Bdd.

J'ai commencé à essayer de déplacer les intrus d'un arbre vers son arbre, mais je ne crois pas pouvoir y arriver... :/ ou au pris de fortes migraines...
Je me demande donc si je vais pas créer autant d'arbres que d'utilisateurs... Je n'aurais du coup plus le problème d'arbres croisés. Et toutes mes bornes seront contigus au sein d'un même arbre.
Le seul défaut c'est que j'aurais autant de racine que d'utilisateurs et donc ça prend un peu de place en Bdd... m'enfin je pense que c'est négligable...

Qu'en pensez-vous ?

Merci d'avance !