Продолжаю тему работы со вложенными множествами в ORM Doctrine.
Сегодня я хочу подробнее остановиться на классе Doctrine_Tree_NestedSet, отвечающем за работу с деревом, а также о выборке данных из дерева.
Класс Doctrine_Tree_NestedSet
Сразу оговорюсь, что этот класс в официальной документации описан не полностью - описание четырех из 11 методов класса отсутствует. Нужно сказать, что эти четыре метода используются внутри класса и для непосредственной работы нам не понадобятся. Описание этих четырех методов я, возможно, добавлю позднее сам, и, следовательно, описание этих методов не будет претендовать на абсолютную правильность. Некоторые из методов этого класса использовались в предыдущей части статьи, например, когда мы извлекали все дерево. Здесь я опишу их более подробно.
Краткое описание методов для работы с деревом класса Doctrine_Tree_NestedSet
(Описание на официальном сайте (англ.))
Возвр.значение Имя метода Описание
void createRoot(object record) - создает корневой узел из указанной записи или из новой записи.
mixed fetchBranch(mixed pk, array options, integer fetchmode) - извлекает ветку из дерева.
void fetchRoot(integer rootId) - извлекает корневой узел.
mixed fetchRoots() - извлекает все корневые узлы.
mixed fetchTree(array options, integer fetchmode) - извлекает дерево.
void getBaseAlias() - Описание отсутствует...
unknown getBaseQuery(array options) - Описание отсутствует...
void resetBaseQuery() - Описание отсутствует...
Doctrine_Query returnQueryWithRootId(object query, mixed rootId, integer root_id) - возвращает обработанный запрос с id корневого узла, если возможно.
void setBaseQuery(Doctrine_Query query) - Описание отсутствует...
void setTableDefinition() - Используется для определения атрибутов, необходимых для организации вложенного множества,
добавляет поля lft и rgt для соответствующих значений левого и правого ключей.
createRoot()
Cоздает корневой узел из указанной записи или из новой записи.
Замечание.
При использовании дерева с несколькими корневыми узлами (hasManyRoots), в качестве аргумента необходимо передавать экземпляр класса Doctrine_Record (по умолчанию = null). Можно передавать либо новый объект, либо существующий, у которого в поле id уже есть значение. В последнем случае id записи будет записано в качестве значения root_id. необходимо использовать числовые значения для полей id и root_id.
Аргументы:
запись - экземпляр класса Doctrine_Record.
fetchBranch()
Извлекает ветку из дерева.
Аргументы:
pk - первичный ключ, который возвращает метод table::find(), узла, для которого будет строиться ветка (дерево).
options - опции - массив array('depth' => глубина ветки);
fetchmode - Одна из констант Doctrine_Core::HYDRATE_* (см. замечание ниже).
Возвращает:
Ветку или false, если ветка не найдена.
Замечание.
Константы Doctrine_Core::HYDRATE_* определяют т.н. hydration ("заполнение") результата. Указание этих констант позволяет получать возвращаемый методом результат не в виде объекта класса Doctrine_Query, а в виде других типов данных.
Для метода fetchBranch() можно использовать константы:
HYDRATE_RECORD - возвращается объект.
HYDRATE_ARRAY - возвращается одномерный массив.
HYDRATE_ARRAY_HIERARCHY - возвращается многомерный массив с глубиной вложенности, определяемой полем level.
fetchRoot()
Извлекает корневой узел
Аргументы:
int - id корневого узла.
Возвращает:
объект - корневой узел.
fetchRoots()
Извлекает все корневые узлы. Если у дерева единственный корневой узел, действует так же, как и метод fetchRoot().
Возвращает:
корневые узлы.
fetchTree()
Извлекает дерево.
Аргументы:
options - опции - массив array('depth' => глубина дерева, 'root_id' => id корневого узла, по умолчанию = 1);
fetchmode - Одна из констант Doctrine_Core::HYDRATE_*.
Возвращает:
дерево или FALSE в случае неудачи.
returnQueryWithRootId()
Возвращает обработанный запрос с вставленным в него в части 'WHERE' id корневого узла:
WHERE `имя поля с id` = 'id корневого узла'или узлов:
'WHERE `имя поля с id` IN (...)
По умолчанию id = 1.Аргументы:
query - запрос (объект класса Doctrine_Query)
root_id - id корневого узла (int), или id корневых узлов, если корневых узлов несколько (в виде массива)
setTableDefinition()
Используется для определения атрибутов, необходимых для организации вложенного множества, добавляет поля lft и rgt для соответствующих значений левого и правого ключей.
Выбрасывает исключение, если атрибуты не были определены.
Примеры применения.
Выборка всего или части дерева произвольной глубины.
Построение всего дерева с помощью метода fetchTree() приводилось в предыдущей части статьи:
$tree = Doctrine_Core::getTable('Menu')->getTree()->fetchTree();
foreach ($tree as $node)
{
echo str_repeat(' ', $node['level'])
. $node['rusname'] . '<br/>';
}
Результат:
Главная
Новости
Новость 1
Новость 2
Новость 3
Новость 4
Лига чемпионов
2011
2010
2012
Календарь
Результаты
Команды
Лига Европы
2011
2010
Здесь замечу, что метод fetchTree() возвращает дерево в виде объекта. Использовав константу Doctrine_Core::HYDRATE_ARRAY, возвратится одномерный массив.
Если нужно возвратить не все уровни дерева, нужно указать глубину в опциях.
Например:
$tree = Doctrine_Core::getTable('Menu')->getTree()->fetchTree(array('depth' => 1));
foreach ($tree as $node)
{
echo str_repeat(' ', $node['level'])
. $node['rusname'] . '<br/>';
}
Результат:
Главная
Новости
Лига чемпионов
Лига Европы
Выборка ветки или ее части произвольной глубины.
Выборка ветки осуществляется с помощью метода fetchBranch().
$node = Doctrine_Core::getTable('Menu')->findOneByRusname('Лига чемпионов');
$branch = Doctrine_Core::getTable('Menu')->getTree()->fetchBranch($node['id']);
foreach ($branch as $node)
{
echo str_repeat(' ', $node['level'])
. $node['rusname'] . '<br/>';
}
Результат:
Лига чемпионов
2011
2010
2012
Календарь
Результаты
Команды
Если нужно возвратить ветку не на всю ее глубину, нужно указать глубину в опциях.
$branch = Doctrine_Core::getTable('Menu')->getTree()->fetchBranch($node['id'], array('depth' => 1));
foreach ($branch as $node)
{
echo str_repeat(' ', $node['level'])
. $node['rusname'] . '<br/>';
}
Результат:
Лига чемпионов
2011
2010
2012
Эту же ветку можно получить в виде массива, если использовать константу Doctrine_Core::HYDRATE_ARRAY (или другие, указанные выше).
$node = Doctrine_Core::getTable('Menu')->findOneByRusname('Лига чемпионов');
$branch = Doctrine_Core::getTable('Menu')->getTree()->fetchBranch($node['id'], array('depth' => 1), Doctrine_Core::HYDRATE_ARRAY);
echo '<pre>' . print_r($branch, 1) . '</pre>';
Результат:
Array
(
[0] => Array
(
[id] => 2
[reference] => page=cl
[rusname] => Лига чемпионов
[lft] => 12
[rgt] => 25
[level] => 1
)
[1] => Array
(
[id] => 9
[reference] => page=cl&year=2011
[rusname] => 2011
[lft] => 13
[rgt] => 14
[level] => 2
)
[2] => Array
(
[id] => 10
[reference] => page=cl&year=2010
[rusname] => 2010
[lft] => 15
[rgt] => 16
[level] => 2
)
[3] => Array
(
[id] => 5
[reference] => page=el&year=2012
[rusname] => 2012
[lft] => 17
[rgt] => 24
[level] => 2
)
)
В связи с дефицитом времени, на сегодня закончу обзор. Впоследствии, скорее всего, буду его корректировать и расширять.
Всем удачи!