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
+
:::