Je dois construire un arbre qui contiendra environ 300 nœuds. L'arbre n'a aucune limite de profondeur. Donc, il peut avoir 3 ou 15 niveaux. Chaque nœud peut avoir un nombre illimité d'enfants.
La priorité est d’obtenir un arbre/sous-arbre complet le plus rapidement possible, mais j’ai également besoin d’ajouter ou de déplacer des nœuds de temps en temps, mais rarement.
Je veux savoir le meilleur moyen de stocker l'arbre dans la base de données et le meilleur moyen de récupérer les données, si possible, en php.
Vous pouvez utiliser un Nested Set Model car il génère des requêtes très efficaces. Consultez Gestion des données hiérarchiques dans MySQL et lisez la section intitulée Nested Set Model.
Si vous utilisez un ORM tel que Doctrine, il inclut des capacités d'ensemble imbriquées .
Il peut être difficile pour certains de saisir les concepts d'ensemble imbriqués de left et right. J'ai constaté qu'en utilisant ces numéros comme analogie pour les numéros de ligne des balises open/close dans un document XML, les gens trouvent qu'il est plus facile à comprendre.
Par exemple, prenons l'exemple de données du lien MySQL ci-dessus:
+-------------+----------------------+-----+-----+
| category_id | name | lft | rgt |
+-------------+----------------------+-----+-----+
| 1 | ELECTRONICS | 1 | 20 |
| 2 | TELEVISIONS | 2 | 9 |
| 3 | TUBE | 3 | 4 |
| 4 | LCD | 5 | 6 |
| 5 | PLASMA | 7 | 8 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 7 | MP3 PLAYERS | 11 | 14 |
| 8 | FLASH | 12 | 13 |
| 9 | CD PLAYERS | 15 | 16 |
| 10 | 2 WAY RADIOS | 17 | 18 |
+-------------+----------------------+-----+-----+
Si vous prenez les champs lft, rgt et les utilisez comme numéros de ligne pour un document XML, vous obtenez:
1. <electronics>
2. <televisions>
3. <tube>
4. </tube>
5. <lcd>
6. </lcd>
7. <plasma>
8. </plasma>
9. </televisions>
10. <portable electronics>
11. <mp3 players>
12. <flash>
13. </flash>
14. </mp3 players>
15. <cd players>
16. </cd players>
17. <2 way radios>
18. </2 way radios>
19. </portable electronics>
20. </electronics>
En le voyant ainsi, il est beaucoup plus facile pour certains de visualiser la hiérarchie d'ensembles imbriquée résultante. Cela permet également de mieux comprendre pourquoi cette approche améliore l'efficacité, car elle permet de sélectionner des nœuds entiers sans recourir à plusieurs requêtes ou jointures.
C'est un excellent article à ce sujet: Gestion des données hiérarchiques dans MySQL . Je l'ai utilisé pendant longtemps.
Si vous avez des capacités mathématiques, vous pouvez vraiment comprendre pourquoi c'est si génial!
<?php
$Host = "localhost";
//Database user name.
$login = "root";
//Database Password.
$dbpass = "";
$dbname = "abc";
$PDO = new PDO("mysql:Host=localhost;dbname=$dbname", "$login", "$dbpass");
$rows = array();
$sql = 'SELECT id, parent_id, name FROM employee';
$query = $PDO->prepare($sql);
$query->execute();
$rows = array();
if (!$query)
{
$error = 'Error fetching page structure, for nav menu generation.';
exit();
}
while($row = $query->fetch(PDO::FETCH_ASSOC)){
if( strcasecmp($row['parent_id'],'null') === 0 || empty($row['parent_id']) ) {
$row['parent_id'] = null;
}
$rows[] = $row;
}
// covert raw result set to tree
$menu = convertAdjacencyListToTree(null,$rows,'id','parent_id','links');
// echo '<pre>',print_r($menu),'</pre>';
// display menu
echo themeMenu($menu,1);
/*
* ------------------------------------------------------------------------------------
* Utility functions
* ------------------------------------------------------------------------------------
*/
/*
* Convert adjacency list to hierarchical tree
*
* @param value of root level parent most likely null or 0
* @param array result
* @param str name of primary key column
* @param str name of parent_id column - most likely parent_id
* @param str name of index that children will reside ie. children, etc
* @return array tree
*/
function convertAdjacencyListToTree($intParentId,&$arrRows,$strIdField,$strParentsIdField,$strNameResolution) {
$arrChildren = array();
for($i=0;$i<count($arrRows);$i++) {
if($intParentId === $arrRows[$i][$strParentsIdField]) {
$arrChildren = array_merge($arrChildren,array_splice($arrRows,$i--,1));
}
}
$intChildren = count($arrChildren);
if($intChildren != 0) {
for($i=0;$i<$intChildren;$i++) {
$arrChildren[$i][$strNameResolution] = convertAdjacencyListToTree($arrChildren[$i][$strIdField],$arrRows,$strIdField,$strParentsIdField,$strNameResolution);
}
}
return $arrChildren;
}
/*
* Theme menu
*
* @param array menu
* @param runner (depth)
* @return str themed menu
*/
function themeMenu($menu,$runner) {
$out = '';
if(empty($menu)) {
return $out;
}
$out.='<ul>';
foreach($menu as $link) {
$out.= sprintf(
'<li class="depth-%u">%s%s</li>'
,$runner
,$link['name']
,themeMenu($link['links'],($runner+1))
);
}
$out.='</ul>';
return $out;
}
?>