I usually fetch NestedSet tree like this:
class ModelTable extends Doctrine_Table
{
/**
* Gets tree elements in one query
*/
public function getMenuTree()
{
$q = $this->createQuery('p')
->orderBy('p.root_id')
->addOrderBy('p.lft');
$tree = $q->execute(array(), Doctrine_Core::HYDRATE_RECORD_HIERARCHY);
return $tree;
}
}
So I can actually display the whole tree while using only one query to the database.. until I try to traverse the tree. For example, if you call a method on a node like this:
$node->getNode()->getAncestors()
Doctrine will build up a whole new query for this (have a look at Doctrine_Node_NestedSet::getAncestors()). Other traversing methods like getChildren() also use DQL. But this is somewhat inefficient, isn't it? Once I fetched the whole tree, I don't want to query the database any more.
Maybe someone has written a driver to do it the right way? (without DQL)
If you only want to fetch the children (which is the most likely, why would you need getAncestors()
to iterate on a tree?), you could also keep the code you showed us as example, and do something like this this:
foreach ($categories->getFirst()->get('__children') as $child) {
// ...
}
This is documented here (hard to find unless you choose to read the whole documentation).
I have once used recursive code on a whole tree with only ONE query.
1015 lib % ack --type="php" "_node" 2011-05-15 14:26:22 greg pts/1
vendor/doctrine/Doctrine/Record.php
94: protected $_node;
814: unset($vars['_node']);
2403: if ( ! isset($this->_node)) {
2404: $this->_node = Doctrine_Node::factory($this,
2410: return $this->_node;
liche ~/source/symfony/1.4/lib/plugins/sfDoctrinePlugin/lib
_node only seems to be set in getNode()
itself, I don't know whether you can hydrate it like any other field, nor how you would do this.
I think that getNode()
should only be used for modifications on the tree.
If you want to display the path from the root, you should use a recursive method to display the tree, whith an argument containing the parent's path . If there is anything else for which you would need the tree functionality, tell us...
UPDATE
I think I eventually got it. You want to display a tree menu AND a breadcrumb, and you want to reuse the data of the menu in the breadcrumb, isn't it?
To display your breadcrumb, you have to recurse on $tree, and display a node if and only if it is an ancestor of the current page. And there is a method for that : isAncestorOf()
.
So "all you have to do" is a template which does something like this:
//module/templates/_breadcrumbElement.php
foreach ($node->get('__children') as $child) :
if ($child->isAncestorOf($pageNode)):
echo link_to($child->getName(), $child->getUrl());
include_partial('module/breadcrumbElement', array('node' => $child, 'pageNode' => $pageNode));
endif;
endforeach;
Feed it the root of your tree and you'll be fine. Hopefully.