I have a model that implements NestedSet behaviour:
Page:
actAs:
NestedSet:
hasManyRoots: true
rootColumnName: root_id
columns:
slug: string(255)
name: string(255)
Example fixtures:
Page:
NestedSet: true
Page_1:
slug: slug1
name: name1
Page_2:
slug: slug2
name: name2
children:
Page_3:
slug: page3
name: name3
I am looking for the easiest way to implement breadcrumb navigation (trail). For example, for Page_3 navigation will look like this:
<a href="page2">name2</a> > <a href="page2/page3>name3</a>
Almost the same as in the other question, but you have to add a 'parentUrl' variable :
//module/templates/_breadcrumbElement.php
foreach ($node->get('__children') as $child) :
if ($child->isAncestorOf($pageNode)):
$currentNodeUrl = $parentUrl . $child->getSlug() . '/';
echo link_to($child->getName(), $currentNodeUrl) . ' > ' ;
include_partial('module/breadcrumbElement', array('node' => $child, 'pageNode' => $pageNode, 'parentUrl' => $currentNodeUrl));
endif;
endforeach;
Feed it the root of your tree as $node
(hydrate it hierarchically), the node of the current page as $pageNode
, and '' as $currentNodeUrl
and add ' > ' and the link to the current page.
Why does this solution use recursion and not getAncestors()
? Because your urls seem to imply recursion.
Since I hate having any kind of logic in templates (and partials), here's my slightly improved version.
//module/templates/_breadcrumbElement.php
<?php foreach ($node as $child): ?>
<li>
<a href="<?php echo $child->getPath($parent) ?>"><?php echo $child->getName() ?></a>
<?php if (count($child->get('__children')) > 0): ?>
<ul>
<?php include_partial('node', array('node' => $child->get('__children'), 'parent' => $child)) ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
So, all the logic for building a url is now in Page::getPath() method.
class Page extends BasePage
{
/**
* Full path to node from root
*
*/
protected $path = false;
public function __toString()
{
return $this->getSlug();
}
public function getPath($parent = null)
{
if (!$this->path)
{
$this->path = join('/', null !== $parent ? array($parent->getPath(), $this) : array($this));
}
return $this->path;
}
}
What I don't like it having to pass $parent to Page::getPath(). It just doesn't make any semantical sense.
Another answer, more simple (and perhaps more efficient), with getAncestors() and recursion:
//module/templates/_breadcrumbElement.php
if ($node = array_pop($nodes)) // stop condition
{
$currentNodeUrl = $parentUrl . $node->getSlug() . '/';
echo link_to($node->getName(), $currentNodeUrl) . ' > ' ;
include_partial('module/breadcrumbElement', array(
'nodes' => $nodes, 'parentUrl' => $currentNodeUrl));
}
Call this with an array of ancestor nodes or find a way to pop a Doctrine_Collection
if you want to use it with getAncestors()
directly.
Again, all your problem comes from the fact that your urls are recursively computed, it would be simpler and faster to display if you had a colum path with the current url (but then you would have to compute, update it), etc... consider doing this if you have more reads than writes (if your tree does not change often).