Tree

← Tree

Ashterix

docs: Doctrine section with Doctrine Behaviors and Doctrine Helper (uk + en)

en/docs/doctrine/doctrine-behaviors/tree.md new file +94 āˆ’0

1 + # Tree
2 +
3 + A materialized-path tree: each node stores its path from the root (`/1/4`), and the hierarchy is assembled in memory from a flat result set.
4 +
5 + ## Entity
6 +
7 + ```php
8 + use ArrayAccess;
9 + use Doctrine\ORM\Mapping as ORM;
10 + use Ufo\DoctrineBehaviors\Contract\Entity\TreeNodeInterface;
11 + use Ufo\DoctrineBehaviors\Model\Tree\TreeNodeTrait;
12 +
13 + #[ORM\Entity(repositoryClass: CategoryRepository::class)]
14 + class Category implements TreeNodeInterface, ArrayAccess
15 + {
16 + use TreeNodeTrait;
17 +
18 + #[ORM\Id, ORM\Column]
19 + private ?int $id = null;
20 +
21 + #[ORM\Column(nullable: true)]
22 + private ?string $name = null;
23 +
24 + public function __toString(): string
25 + {
26 + return (string) $this->name;
27 + }
28 +
29 + public function getId(): ?int
30 + {
31 + return $this->id;
32 + }
33 +
34 + public function setId(int $id): void
35 + {
36 + $this->id = $id;
37 + }
38 + }
39 + ```
40 +
41 + * The bundle maps `materializedPath` (string 255). `parentNode` and `childNodes` are not persisted — `buildTree()` fills them.
42 + * `getNodeId()` returns `getId()` by default. The identifier is needed **before** the node is added to a tree, otherwise `setChildNodeOf()` throws `TreeException` — that is why the example sets the id manually.
43 + * `ArrayAccess` is optional: it gives `$node[0]` access to child nodes, and `$node[] = $child` adds a child node.
44 +
45 + ## Repository
46 +
47 + ```php
48 + use Doctrine\ORM\EntityRepository;
49 + use Ufo\DoctrineBehaviors\ORM\Tree\TreeTrait;
50 +
51 + class CategoryRepository extends EntityRepository
52 + {
53 + use TreeTrait;
54 + }
55 + ```
56 +
57 + | `TreeTrait` method | What it does |
58 + | --- | --- |
59 + | `getTree(string $path = '')` | Selects nodes by path and returns the first one (the root) with the tree built, or `null`. |
60 + | `getFlatTree(string $path)` / `getFlatTreeQB()` | Flat list / QueryBuilder of nodes sorted by `materializedPath`. |
61 + | `getRootNodes()` / `getRootNodesQB()` | Nodes with an empty `materializedPath`. |
62 + | `getTreeExceptNodeAndItsChildrenQB(TreeNodeInterface $node)` | All nodes except the given one and its descendants (e.g. to pick a new parent). |
63 + | `buildTree(array $results)` | Builds a tree from a ready flat result set. |
64 + | `addFlatTreeConditions(QueryBuilder $qb, array $extraParams)` | Empty hook for extra query conditions. |
65 +
66 + `getTreeExceptNodeAndItsChildrenQB()` and `getFlatTreeQB()` refer to the `id` field, so the identifier must have exactly that name.
67 +
68 + ## Usage
69 +
70 + ```php
71 + $root = new Category();
72 + $root->setId(1);
73 +
74 + $child = new Category();
75 + $child->setId(2);
76 + $child->setChildNodeOf($root); // materializedPath = '/1'
77 +
78 + $entityManager->persist($root);
79 + $entityManager->persist($child);
80 + $entityManager->flush();
81 +
82 + $tree = $categoryRepository->getTree();
83 + $tree->getChildNodes(); // Collection
84 + $tree[0]; // first child node (with ArrayAccess)
85 + $tree->isRootNode(); // true
86 + $tree[0]->isLeafNode(); // true
87 + $tree[0]->getNodeLevel(); // 2
88 + ```
89 +
90 + Other node methods: `getParentNode()`, `getRootNode()`, `isChildNodeOf()`, `isIndirectChildNodeOf()`, `getRealMaterializedPath()`, `toArray()`, `toFlatArray()`, `toJson()`. The path separator (`/`) is changed by overriding the static `getMaterializedPathSeparator()`.
91 +
92 + :::warning
93 + A new node has an empty path, while `setChildNodeOf(null)` writes the path `/`. Both are treated as roots (`isRootNode()`), but `getRootNodes()` finds only nodes with an empty path — see [Known issues](/docs/doctrine/doctrine-behaviors/known_issues/open).
94 + :::