# sfPropelActAsNestedSetBehaviorPlugin plugin The `sfPropelActAsNestedSetBehaviorPlugin` is a symfony plugin that provides nested set capabilities to Propel objects. Nested sets (aka modified preorder tree traversal) is a very efficient way (in terms of performances) to browse and edit a tree like structure in an RDBMS. You can read [a good introduction to nested sets](http://dev.mysql.com/tech-resources/articles/hierarchical-data.html) on MySQL developers' zone. ## Features * Fully unit tested * Possibility to store multiple trees in the same table * Atomic operations ## Installation * Install the plugin symfony plugin-install http://plugins.symfony-project.com/sfPropelActAsNestedSetBehaviorPlugin * Enable Propel behavior support in `propel.ini`: propel.builder.AddBehaviors = true If you have to enable the behavior support, rebuild your model: symfony propel-build-model * Enable the behavior for one of your Propel model: // lib/model/ForumPost.php class ForumPost { } $columns_map = array('left' => ForumPost::TREE_LEFT, 'right' => ForumPost::TREE_RIGHT, 'parent' => ForumPost::TREE_PARENT, 'scope' => ForumPost::TOPIC_ID); sfPropelBehavior::add('ForumPost', array('actasnestedset' => array('columns' => $columns_map))); The *column map* is used by the behavior to know which columns hold information it needs : * left : Model column holding nested set left value for a row * right : Model column holding nested set right value for a row * parent : Model column holding row's parent id (this is necessary because we use adjacency list tree traversal for some methods) * scope : Model column holding row's scope id. The scope is used to differenciate trees stored in the same table ## Usage Simple tree creation : [php] $root = new ForumPost(); $root->makeRoot(); $root->save(); $p1 = new ForumPost(); $p1->insertAsFirstChildOf($root); $p1->save(); $p2 = new ForumPost(); $p2->insertAsFirstChildOf($p1); $p2->save(); /* * Resulting tree : * * ROOT * |- P1 * |- P2 */ Multiple trees in a single table : [php] $root1 = new ForumPost(); $root1->makeRoot(); $root1->setTopicId(1); $root1->save(); $root2 = new ForumPost(); $root2->makeRoot(); $root2->setTopicId(2); $root2->save(); $p1 = new ForumPost(); $p1->insertAsFirstChildOf($root1); $p1->save(); $p2 = new ForumPost(); $p2->insertAsFirstChildOf($root2); $p2->save(); /* * Resulting trees : * * ROOT1 * |- P1 * * ROOT2 * |- P2 */ ## Public API Enabling the behaviors adds the following method to the Propel objects : ### Insertion methods * `void insertAsFirstChildOf(BaseObject $dest_node)` : Inserts node as first child of given node. * `void insertAsLastChildOf(BaseObject $dest_node)` : Inserts node as last child of given node. * `void insertAsNextSiblingOf(BaseObject $dest_node)` : Inserts node as next sibling of given node. * `void insertAsPrevSiblingOf(BaseObject $dest_node)` : Inserts node as previous sibling of given node. ### Informational methods * `bool hasChildren()` : Returns true if given node as one or several children. * `bool isRoot()` : Returns true if given node is a root node. * `bool hasParent()` : Returns true if given node has a parent node. * `bool hasNextSibling()` : Returns true if given node has a next sibling. * `bool hasPrevSibling()` : Returns true if given node has a previous sibling. * `bool isLeaf()` : Returns true if given node does not have children. * `integer getNumberOfChildren()` : Returns given node number of direct children. * `integer getNumberOfDescendants()` : Returns given node number of descendants (n level). * `integer getLevel()` : Returns given node level. ### Node retrieval methods * `array getChildren($peer_method = 'doSelect')` : Returns given node direct children. * `array getDescendants($peer_method = 'doSelect')` : Returns given node descendants (n level). * `BaseObject retrieveNextSibling()` : Returns given node next sibling. * `BaseObject retrievePrevSibling()` : Returns given node previous sibling. * `BaseObject retrieveFirstChild()` : **NOT IMPLEMENTED YET** * `BaseObject retrieveLastChild()` : **NOT IMPLEMENTED YET** * `array getPath()` : **NOT IMPLEMENTED YET** ### Tree modification methods * `void moveToFirstChildOf(BaseObject $dest_node)` : Moves node to first child of given node. * `void moveToLastChildOf(BaseObject $dest_node)` : Moves node to last child of given node. * `void moveToNextSiblingOf()` : **NOT IMPLEMENTED YET** * `void moveToPrevSiblingOf()` : **NOT IMPLEMENTED YET** * `void deleteChildren()` : **NOT IMPLEMENTED YET** * `void deleteTree()` : **NOT IMPLEMENTED YET** ### Helper methods * `void makeRoot()` : Sets node properties to make it a root node. * `BaseObject reload()` : Returns an up to date version of node