Lazy Загрузка с Doctrine2 и Symfony2 с использованием DQL
У меня есть древовидная структура с родительским полем. В настоящее время я пытаюсь заставить все родительские узлы отображать путь к текущему node.
В основном я выполняю цикл while для обработки всех узлов.
$current = $node->getParent();
while($current) {
// do something
$current = $current->getParent();
}
Работает метод findById
по умолчанию. Поскольку у объекта есть агрегированные поля, я использую специальный метод репозитория, чтобы загрузить все базовые поля с одним запросом.
public function findNodeByIdWithMeta($id) {
return $this->getEntityManager()
->createQuery('
SELECT p, a, c, cc, ca, pp FROM
TestingNestedObjectBundle:NestedObject p
JOIN p.actions a
LEFT JOIN p.children c
LEFT JOIN c.children cc
LEFT JOIN c.actions ca
LEFT JOIN p.parent pp
WHERE p.id = :id
')
->setParameter('id', $id)
->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
)
->getOneOrNullResult();
}
С помощью этого кода загрузка родителей не удалась. Я получаю только непосредственного родителя (адрес LEFT JOIN p.parent pp
), но не родители выше. Например. $node->getParent()->getParent()
возвращает null
.
Что не так с моим кодом? Не понял ли я ленивую загрузку?
Большое спасибо,
Hacksteak
Ответы
Ответ 1
Похоже, что вы используете модель смежности для хранения деревьев в реляционной базе данных. Это, в свою очередь, означает, что вам потребуется соединение для каждого уровня, чтобы получить всех предков с одним запросом.
Поскольку вы уже используете Библиотека расширений Doctrine, я рекомендую посмотреть Tree.
Ответ 2
В моем ответе не используется DQL и вместо этого создается NestedSetManager, который имеет доступ к вашему соединению DBAL, поэтому вы можете использовать SQL. Я никогда не чувствовал, что ORM отлично справился с логикой запросов NestedSets.
С помощью NestedSetManager
вы можете написать кучу чистых методов, и это очень просто, потому что все эти запросы хорошо документированы. См. эту ссылку. Некоторые из методов моего NestedSetManager:
setNode();
setRoot();
loadNestedSet();
moveNodeUp();
modeNodeDown();
getRootNode();
addNodeSibling();
getNodesByDepth();
getParents();
getNodePath();
childExists();
addChildToNode();
renameNode();
deleteNode();
// And many more
У вас может быть мяч и создать много возможностей для создания NestedSet, если вы не связаны с некоторой сложной функциональностью ORM.
Также - Symfony2 делает все это действительно очень просто. Вы создаете файл класса NestedSetManager и ссылаетесь на него в свой Service.yml и передаете свое соединение Dbal. Мой выглядит так:
services:
manager.nestedset:
class: Acme\CoreBundle\Manager\NestedSetManager
arguments: [ @database_connection ]
вы можете получить доступ к своим вложенным наборам с помощью:
$path = $this->get('manager.nestedset')->setNode(4)->getNodePath(); // in your controller
Мораль истории, ORM/NestedSets заставила меня запугивать, и это решение работает очень хорошо. Если вы вынуждены использовать DQL и не имеете других параметров, этот ответ, вероятно, не будет приемлемым.