= 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 [http://dev.mysql.com/tech-resources/articles/hierarchical-data.html a good introduction to nested sets] on MySQL developers' zone. == Features == * Fully unit tested * Store multiple trees in the same table == 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('MyClass', 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($p2); $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