Reputation: 121
I'm currently learning symfony1.4 and am trying to navigate around the nested sets feature of doctrine with limited success. i have a category table using the nested set behaviour with mutliple trees. my category trees data can be up to six deep...
Categories:
columns:
category_name:
type: string(40)
actAs:
NestedSet:
hasManyRoots: true
rootColumnName: parent_id
these categories are for products, i'd like a page to filter through the categories to reach a leaf in order to display that categories products e.g. display roots, if a root is selected, then display its children, then if a child is selected display its children, etc until you select a leaf node. here's an image which may describe it better
I was trying to build a 2 dimensional array of the filtered categories, to pass to the view... with no success. any solutions would be helpful as my head is fried!
many thanks,
rob.
Upvotes: 1
Views: 895
Reputation: 23255
Rephrasing your problem : you need (every ancestor of your node and your node) and their siblings.
Other rephrasing : you need every ancestor of your node along with all of their children (but not all of their descendants)
And don't think you should have hasManyRoots set to true, unless you have several shops. Now let's assume you have one root, which you could name 'shop'
Here is a third, more simple rephrasing: all you need is the children of every ancestor of your node (and love, according to the Beatles).
Getting the ancestors as an array (see why in the last §) is easy :
$this->ancestors = $currentCategory->getNode()->getAncestors()->getData();
Now let's build the query which will give you what you need. Let's assume your model is named Category, not Categories (it really should).
$q = CategoryTable::getInstance()->createQuery('c');
foreach ($this->ancestors as $ancestor)
{
// should work thanks to AND priority over OR
$q->orWhere('c.level = ?' $ancestor->getLevel() + 1)
->andWhere('c.lft > ?' $ancestor->getLeftValue())
->andWhere('c.rgt < ?' $ancestor->getRightValue())
}
If you don't understand what this last thing is, then you probably need to read this excellent article about adjacency list model vs nested set model
Ok now that you have your query, let's fetch the results:
$this->categoryTree = $q->execute(
array(),
Doctrine_Core::HYDRATE_RECORD_HIERARCHY);
O_o wait... what was that last parameter about? You probably didn't hear about it when reading the doctrine documentation about nested set. That's because it is documented on the page about data hydrators. This really sucks, because HYDRATE_RECORD_HIERARCHY
is very interesting when working with nested sets. Now what you have everything you need in $categoryTree as a hierarchy. With only 2 requests no matter how deep your tree is! I guess one could write it in one request, but I don't see how.
Note: there also is Doctrine_Core::HYDRATE_ARRAY_HIERARCHY
, which hydrates to a hierarchical array, which is much faster. You can use it if you don't need to call methods that provide stuff that is not part of your object, or that is computed at run time. You'll just have to use array instead of object notation in your template (e.g. $categoryTree['children']
)
Now in your _menu.php
template, here is what you can do:
<?php
array_shift($ancestors);
include_partial('level', array(
'children' => $categoryTree->get('__children'),
'ancestors' => $ancestors
);
And in _level.php
:
<ul>
<?php $selectedChild = array_shift($ancestors);
foreach ($children as $child):
if ($isSelected = ($child->getId() == $selectedChild->getId())):
$grandChildren = $child->get('__children');
endif; ?>
<li<?php if ($isSelected):?> class="selected"<?php endif?>>
<?php echo $child->getName() ?>
</li>
<?php endforeach ?>:
</ul>
<?php if (count($ancestors)):
// RecursiviRecurRecuRecursiRRecursivityecursivityvityrsivitysivityty
include_partial('level', array(
'children' => $grandChildren,
'ancestors' => $ancestors
);
endif; ?>
I just wrote this without testing anything, so it will probably not work right from the start. Just keep me posted with the problems you have. Good luck!
Upvotes: 5