setTableName('menu'); // add this your table definition to set the table as NestedSet tree implementation $this->actsAsTree('NestedSet'); // you do not need to add any columns specific to the nested set implementation, these are added for you $this->hasColumn("name","string",30); } // this __toString() function is used to get the name for the path, see node::getPath public function __toString() { return $this->get('name'); } } // set connections to database $dsn = 'mysql:dbname=nestedset;host=localhost'; $user = 'user'; $password = 'pass'; try { $dbh = new PDO($dsn, $user, $password); } catch (PDOException $e) { echo 'Connection failed: ' . $e->getMessage(); } $manager = Doctrine_Manager::getInstance(); $conn = $manager->openConnection($dbh); // create root $root = new Menu(); $root->set('name', 'root'); $manager->getTable('Menu')->getTree()->createRoot($root); // build tree $two = new Menu(); $two->set('name', '2'); $root->getNode()->addChild($two); $one = new Menu(); $one->set('name', '1'); $one->getNode()->insertAsPrevSiblingOf($two); // refresh as node's lft and rgt values have changed, zYne, can we automate this? $two->refresh(); $three = new Menu(); $three->set('name', '3'); $three->getNode()->insertAsNextSiblingOf($two); $two->refresh(); $one_one = new Menu(); $one_one->set('name', '1.1'); $one_one->getNode()->insertAsFirstChildOf($one); $one->refresh(); $one_two = new Menu(); $one_two->set('name', '1.2'); $one_two->getNode()->insertAsLastChildOf($one); $one_two->refresh(); $one_two_one = new Menu(); $one_two_one->set('name', '1.2.1'); $one_two->getNode()->addChild($one_two_one); $root->refresh(); $four = new Menu(); $four->set('name', '4'); $root->getNode()->addChild($four); $root->refresh(); $five = new Menu(); $five->set('name', '5'); $root->getNode()->addChild($five); $root->refresh(); $six = new Menu(); $six->set('name', '6'); $root->getNode()->addChild($six); output_message('initial tree'); output_tree($root); $one_one->refresh(); $six->set('name', '1.0 (was 6)'); $six->getNode()->moveAsPrevSiblingOf($one_one); $one_two->refresh(); $five->refresh(); $five->set('name', '1.3 (was 5)'); $five->getNode()->moveAsNextSiblingOf($one_two); $one_one->refresh(); $four->refresh(); $four->set('name', '1.1.1 (was 4)'); $four->getNode()->moveAsFirstChildOf($one_one); $root->refresh(); $one_two_one->refresh(); $one_two_one->set('name', 'last (was 1.2.1)'); $one_two_one->getNode()->moveAsLastChildOf($root); output_message('transformed tree'); output_tree($root); $one_one->refresh(); $one_one->deleteNode(); output_message('delete 1.1'); output_tree($root); // now test fetching root $tree_root = $manager->getTable('Menu')->getTree()->findRoot(); output_message('testing fetch root and outputting tree from the root node'); output_tree($tree_root); // now test fetching the tree output_message('testing fetching entire tree using tree::fetchTree()'); $tree = $manager->getTable('Menu')->getTree()->fetchTree(); while($node = $tree->next()) { output_node($node); } // now test fetching the tree output_message('testing fetching entire tree using tree::fetchTree(), excluding root node'); $tree = $manager->getTable('Menu')->getTree()->fetchTree(array('include_record' => false)); while($node = $tree->next()) { output_node($node); } // now test fetching the branch output_message('testing fetching branch for 1, using tree::fetchBranch()'); $one->refresh(); $branch = $manager->getTable('Menu')->getTree()->fetchBranch($one->get('id')); while($node = $branch->next()) { output_node($node); } // now test fetching the tree output_message('testing fetching branch for 1, using tree::fetchBranch() excluding node 1'); $tree = $manager->getTable('Menu')->getTree()->fetchBranch($one->get('id'), array('include_record' => false)); while($node = $tree->next()) { output_node($node); } // now perform some tests output_message('descendants for 1'); $descendants = $one->getNode()->getDescendants(); while($descendant = $descendants->next()) { output_node($descendant); } // move one and children under two $two->refresh(); $one->getNode()->moveAsFirstChildOf($two); output_message('moved one as first child of 2'); output_tree($root); output_message('descendants for 2'); $two->refresh(); $descendants = $two->getNode()->getDescendants(); while($descendant = $descendants->next()) { output_node($descendant); } output_message('number descendants for 2'); echo $two->getNode()->getNumberDescendants() .'
'; output_message('children for 2 (notice excludes children of children, known as descendants)'); $children = $two->getNode()->getChildren(); while($child = $children->next()) { output_node($child); } output_message('number children for 2'); echo $two->getNode()->getNumberChildren() .'
'; output_message('path to 1'); $path = $one->getNode()->getPath(' > '); echo $path .'
'; output_message('path to 1 (including 1)'); $path = $one->getNode()->getPath(' > ', true); echo $path .'
'; output_message('1 has parent'); $hasParent = $one->getNode()->hasParent(); $msg = $hasParent ? 'true' : 'false'; echo $msg . '
'; output_message('parent to 1'); $parent = $one->getNode()->getParent(); if($parent->exists()) { echo $parent->get('name') .'
'; } output_message('root isRoot?'); $isRoot = $root->getNode()->isRoot(); $msg = $isRoot ? 'true' : 'false'; echo $msg . '
'; output_message('one isRoot?'); $isRoot = $one->getNode()->isRoot(); $msg = $isRoot ? 'true' : 'false'; echo $msg . '
'; output_message('root hasParent'); $hasParent = $root->getNode()->hasParent(); $msg = $hasParent ? 'true' : 'false'; echo $msg . '
'; output_message('root getParent'); $parent = $root->getNode()->getParent(); if($parent->exists()) { echo $parent->get('name') .'
'; } output_message('get first child of root'); $record = $root->getNode()->getFirstChild(); if($record->exists()) { echo $record->get('name') .'
'; } output_message('get last child of root'); $record = $root->getNode()->getLastChild(); if($record->exists()) { echo $record->get('name') .'
'; } $one_two->refresh(); output_message('get prev sibling of 1.2'); $record = $one_two->getNode()->getPrevSibling(); if($record->exists()) { echo $record->get('name') .'
'; } output_message('get next sibling of 1.2'); $record = $one_two->getNode()->getNextSibling(); if($record->exists()) { echo $record->get('name') .'
'; } output_message('siblings of 1.2'); $siblings = $one_two->getNode()->getSiblings(); foreach($siblings as $sibling) { if($sibling->exists()) echo $sibling->get('name') .'
'; } output_message('siblings of 1.2 (including 1.2)'); $siblings = $one_two->getNode()->getSiblings(true); foreach($siblings as $sibling) { if($sibling->exists()) echo $sibling->get('name') .'
'; } $new = new Menu(); $new->set('name', 'parent of 1.2'); $new->getNode()->insertAsParentOf($one_two); output_message('added a parent to 1.2'); output_tree($root); try { $dummy = new Menu(); $dummy->set('name', 'dummy'); $dummy->save(); } catch (Doctrine_Exception $e) { output_message('You cannot save a node unless it is in the tree'); } try { $fake = new Menu(); $fake->set('name', 'dummy'); $fake->set('lft', 200); $fake->set('rgt', 1); $fake->save(); } catch (Doctrine_Exception $e) { output_message('You cannot save a node with bad lft and rgt values'); } // check last remaining tests output_message('New parent is descendant of 1'); $one->refresh(); $res = $new->getNode()->isDescendantOf($one); $msg = $res ? 'true' : 'false'; echo $msg . '
'; output_message('New parent is descendant of 2'); $two->refresh(); $res = $new->getNode()->isDescendantOf($two); $msg = $res ? 'true' : 'false'; echo $msg . '
'; output_message('New parent is descendant of 1.2'); $one_two->refresh(); $res = $new->getNode()->isDescendantOf($one_two); $msg = $res ? 'true' : 'false'; echo $msg . '
'; output_message('New parent is descendant of or equal to 1'); $one->refresh(); $res = $new->getNode()->isDescendantOfOrEqualTo($one); $msg = $res ? 'true' : 'false'; echo $msg . '
'; output_message('New parent is descendant of or equal to 1.2'); $one_two->refresh(); $res = $new->getNode()->isDescendantOfOrEqualTo($one_two); $msg = $res ? 'true' : 'false'; echo $msg . '
'; output_message('New parent is descendant of or equal to 1.3'); $five->refresh(); $res = $new->getNode()->isDescendantOfOrEqualTo($new); $msg = $res ? 'true' : 'false'; echo $msg . '
'; function output_tree($root) { // display tree // first we must refresh the node as the tree has been transformed $root->refresh(); // next we must get the iterator to traverse the tree from the root node $traverse = $root->getNode()->traverse(); output_node($root); // now we traverse the tree and output the menu items while($item = $traverse->next()) { output_node($item); } unset($traverse); } function output_node($record) { echo str_repeat('-', $record->getNode()->getLevel()) . $record->get('name') . ' (has children:'.$record->getNode()->hasChildren().') '. ' (is leaf:'.$record->getNode()->isLeaf().') '.'
'; } function output_message($msg) { echo "
$msg".'
'; }