Подтвердить что ты не робот

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

4b9b3361

Ответ 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 и не имеете других параметров, этот ответ, вероятно, не будет приемлемым.