Tree
Дерево на основі materialized path: кожен вузол зберігає шлях від кореня (/1/4), а ієрархія збирається в пам'яті з плоскої вибірки.
Сутність
use ArrayAccess;
use Doctrine\ORM\Mapping as ORM;
use Ufo\DoctrineBehaviors\Contract\Entity\TreeNodeInterface;
use Ufo\DoctrineBehaviors\Model\Tree\TreeNodeTrait;
#[ORM\Entity(repositoryClass: CategoryRepository::class)]
class Category implements TreeNodeInterface, ArrayAccess
{
use TreeNodeTrait;
#[ORM\Id, ORM\Column]
private ?int $id = null;
#[ORM\Column(nullable: true)]
private ?string $name = null;
public function __toString(): string
{
return (string) $this->name;
}
public function getId(): ?int
{
return $this->id;
}
public function setId(int $id): void
{
$this->id = $id;
}
}
- Бандл мапить
materializedPath(рядок 255).parentNodeіchildNodesне зберігаються — їх заповнюєbuildTree(). getNodeId()за замовчуванням повертаєgetId(). Ідентифікатор потрібен до того, як вузол додають у дерево, інакшеsetChildNodeOf()кидаєTreeException— тому в прикладі id задається вручну.ArrayAccessнеобов'язковий: він дає доступ$node[0]до дочірніх вузлів, а$node[] = $childдодає дочірній вузол.
Репозиторій
use Doctrine\ORM\EntityRepository;
use Ufo\DoctrineBehaviors\ORM\Tree\TreeTrait;
class CategoryRepository extends EntityRepository
{
use TreeTrait;
}
Метод TreeTrait |
Що робить |
|---|---|
getTree(string $path = '') |
Вибирає вузли за шляхом і повертає перший (корінь) із побудованим деревом або null. |
getFlatTree(string $path) / getFlatTreeQB() |
Плоский список / QueryBuilder вузлів, відсортованих за materializedPath. |
getRootNodes() / getRootNodesQB() |
Вузли з порожнім materializedPath. |
getTreeExceptNodeAndItsChildrenQB(TreeNodeInterface $node) |
Усі вузли, крім заданого і його нащадків (напр., для вибору нового батька). |
buildTree(array $results) |
Будує дерево з готової плоскої вибірки. |
addFlatTreeConditions(QueryBuilder $qb, array $extraParams) |
Порожній hook для додаткових умов вибірки. |
getTreeExceptNodeAndItsChildrenQB() і getFlatTreeQB() звертаються до поля id, тож ідентифікатор має називатися саме так.
Використання
$root = new Category();
$root->setId(1);
$child = new Category();
$child->setId(2);
$child->setChildNodeOf($root); // materializedPath = '/1'
$entityManager->persist($root);
$entityManager->persist($child);
$entityManager->flush();
$tree = $categoryRepository->getTree();
$tree->getChildNodes(); // Collection
$tree[0]; // перший дочірній вузол (з ArrayAccess)
$tree->isRootNode(); // true
$tree[0]->isLeafNode(); // true
$tree[0]->getNodeLevel(); // 2
Інші методи вузла: getParentNode(), getRootNode(), isChildNodeOf(), isIndirectChildNodeOf(), getRealMaterializedPath(), toArray(), toFlatArray(), toJson(). Роздільник шляху (/) змінюється перевизначенням статичного getMaterializedPathSeparator().
Новий вузол має порожній шлях, а setChildNodeOf(null) записує шлях /. Обидва вважаються коренями (isRootNode()), але getRootNodes() знаходить лише вузли з порожнім шляхом — див. Відомі помилки.