Tree: выборки по дереву
Методы обхода DevBx\Core\Internals\Tree. Все возвращают выборку DataManager, и все принимают
привычный $parameters — select, filter, order, limit.
Вниз по дереву
$rs = CategoryTable::getChildren($id, ['select' => ['ID', 'NAME']]); // прямые потомки
$rs = CategoryTable::getChildren(0); // первый уровень дерева
$rs = CategoryTable::getSubTree($id); // узел со всеми потомками
$rs = CategoryTable::getSubTree(0); // всё дерево целиком
getSubTree по умолчанию сортирует по левой границе — то есть в порядке обхода, в котором
родитель идёт перед своими потомками. Это ровно тот порядок, который нужен для вывода дерева
списком.
Вверх по дереву
$rs = CategoryTable::getPathToNode($id, ['select' => ['ID', 'NAME']]);
$rs = CategoryTable::getPathToNode($id, $parameters, ['SHOW_LEAF' => false]);
Цепочка родителей от корня к узлу, порядок — от корня. SHOW_LEAF => false убирает из ответа
сам узел: так удобно строить хлебные крошки, где последнее звено не ссылка.
Родственные методы:
| Метод | Что делает |
|---|---|
getPathToNodeByCondition($filter, $parameters, $behaviour) |
то же, но узел ищется фильтром, а не по номеру |
getPathToMultipleNodes($nodeInfo, $parameters, $behaviour) |
цепочки сразу к нескольким узлам, ответ — DB\ArrayResult |
getDeepestCommonParent($nodeInfo, $parameters) |
ближайший общий родитель набора узлов |
$nodeInfo у последних двух — массив массивов, в каждом должны быть LEFT_MARGIN и
RIGHT_MARGIN. Это не номера узлов: границы вызывающий читает сам, зато метод не идёт за ними
в базу.
Ветка с раскрытыми уровнями
$rs = CategoryTable::getParentTree($id);
$rs = CategoryTable::getParentTree($id, [], ['SHOW_CHILDREN' => false, 'START_FROM' => $rootId]);
Отдаёт цепочку родителей вместе с соседями на каждом уровне — то, что нужно дереву в интерфейсе,
когда раскрыт один узел, а остальные ветки свёрнуты до своего уровня. SHOW_CHILDREN => false
убирает потомков самого узла, START_FROM начинает раскрытие не от корня, а от указанного узла.
Проверки и подзапрос
CategoryTable::checkNodeIsParentOfNodeById($parentId, $childId);
CategoryTable::checkNodeIsParentOfNodeById($parentId, $childId, ['CHECK_DIRECT' => true]);
$sql = CategoryTable::getSubtreeRangeSqlForNode($id);
Первый отвечает, является ли узел родителем другого; с CHECK_DIRECT — только прямым. Узла нет
— SystemException.
Второй отдаёт подзапрос с номерами поддерева, чтобы подставить его в чужой фильтр. Помечен как служебный: подпись может измениться.
Что учесть
Свой фильтр по границам будет затёрт. Методы дописывают в $parameters['filter'] условия по
LEFT_MARGIN, RIGHT_MARGIN и DEPTH_LEVEL. Своё условие по этим полям в том же фильтре не
переживёт вызова.
Порядок задаётся не везде. getSubTree, getPathToNode и getParentTree ставят сортировку
по левой границе, если своя не задана. У getChildren умолчания нет — порядок будет тот, что
вернёт база.
У класса есть свои expect*-методы. expectInteger, expectIntegerPositive и другие
повторяют DevBx\Core\Assert и объявлены прямо в Tree — на них опираются его собственные
проверки. В своём коде берите Assert: у него это и есть ответственность.