Aller au contenu

Arbres

phil/doctrine-behaviours
TypeGenreRôle
Contract\TreeNodeinterfacetreeId(), treeParent(), treePath(), treeDepth(), placeInTree()
TreeNodeEntitytraitLes colonnes tree_path (750 caractères) et tree_depth, nullables
TreeListenerécouteur : postPersist, onFlush, postFlushPlace les nœuds et déplace les branches
Treelecteurroots(), children(), descendants(), ancestors(), hierarchy()
Branchfinal readonly classUn nœud et les branches sous lui
InvalidTreeLogicExceptionUne place qu'un nœud ne peut pas prendre

Chemins

Le chemin d'un nœud est la suite des identifiants de sa racine jusqu'à lui : /1/4/9/ pour 9 sous 4 sous la racine 1, à la profondeur 2. L'entité mappe son propre identifiant et son parent ; treeId() donne l'identifiant, qui ne contient aucune barre oblique, et treeParent() le parent. Le chemin est écrit une fois le nœud inséré : un identifiant que génère la base convient aussi bien qu'un identifiant que donne l'appelant. L'entité indexe tree_path :

#[ORM\Index(columns: ['tree_path'])]

Déplacements

Un nœud dont le parent a changé prend son nouveau chemin au vidage, et sa branche suit : les descendants déjà chargés sont écrits par le vidage, les autres par un seul UPDATE une fois qu'il est fini. Un nœud ne peut aller ni sous lui-même ni sous l'un de ses descendants, ni sous un parent qui n'a pas encore de chemin : chaque cas est une InvalidTree. Un nœud sans chemin — une ligne plus ancienne que le comportement — n'en a pas tant qu'on ne lui en donne pas.

Les déplacements attendent dans l'écouteur de onFlush à postFlush, et chaque vidage commence sans aucun : ceux d'un vidage qui a échoué — une contrainte, un interblocage — sont abandonnés avec lui, jamais appliqués par le suivant. TreeListener::reset() les oublie aussi ; le bundle l'étiquette kernel.reset.

Lecture

$tree->roots(Category::class);
$tree->children($games);                                 // one level, by depth: the rest of the branch is not read
$tree->descendants($games);                              // none while the node has no path
$tree->ancestors($chess);                                // the root down to the parent
$tree->hierarchy(Category::class);                       // every root, each with its branches
$tree->hierarchy($games, fn ($a, $b) => $a->name <=> $b->name);

Chaque liste suit l'ordre des chemins : un parent avant ses enfants, les nœuds frères dans l'ordre de leurs identifiants ; hierarchy() ordonne les frères selon la comparaison donnée.