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 !
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 :
[list]
[*] 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...)[/list]
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).
[b]/!\ 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 ;)[/b]
[php]<?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;
}
}
[/php]
Bon amusement !