我有一个父字段的树结构。目前,我正在尝试让所有父节点显示当前节点的路径。
基本上我正在做一个while-loop来处理所有节点。
$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
看起来您正在使用邻接模型在关系数据库中存储树。这反过来意味着,您将需要为每个级别进行连接,以便通过单个查询获得所有祖先。
当你已经在使用Doctrine扩展库时,我建议你看看Tree组件。
我的答案涉及不使用DQL,而是创建一个可以访问DBAL连接的NestedSetManager,以便您可以使用SQL。我从来没有觉得ORM在NestedSets查询逻辑方面做得很好。
使用NestedSetManager
,您可以编写一堆干净的方法,这真的很简单,因为所有这些查询都有很好的文档。请看这个链接。我的NestedSetManager中的一些方法是:
setNode();
setRoot();
loadNestedSet();
moveNodeUp();
modeNodeDown();
getRootNode();
addNodeSibling();
getNodesByDepth();
getParents();
getNodePath();
childExists();
addChildToNode();
renameNode();
deleteNode();
// And many more
如果你不被ORM的复杂功能所束缚,你可以有一个球,并创建大量的create NestedSet功能。
还有——Symfony2使这一切变得非常非常容易。你创建你的NestedSetManager类文件并在你的服务中引用它。并传入您的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并且没有其他选择,那么这个答案可能是不可接受的。