1

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.

1 Answer 1

5

Rephrasing

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).

Fetching the data

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'])

Displaying the data

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!

Sign up to request clarification or add additional context in comments.

6 Comments

many thanks greg!! thanks for taking the time to give such a detailed responce. i created my stack overflow account using my facebook account and for some reason wasn't emailed to tell me of your reply... i'll give it a go and let you know how i get on.
Great! You can update your question with the code you'd come up with if your stuck!
Helloooooooooo? I don't even get an upvote for this answer which took me some time to write, and I don't hear from you. What happened?
I'm not the OP, but you've definately earned an upvote from me. Never heard about hierarchy hidration. A 2 minutes well spent reading your answer!
thanks, I think this one of the best answers I wrote here, but I get most of my reputation on short answers to simple questions on the php tag... too bad! Thanks again anyway.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.