123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- = 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
- * Possibility to store multiple trees in the same table
- * Atomic operations
- * Also maintains adjacency list properties, making your classes compatible with code based on this tree traversal methodology
-
- == Limitations ==
- As of now, the plugin is known to work with MySQL and PostgreSQL (in trunk). Other RDBMS may work but without any guaranty.
- Patches are welcome, of course :)
- == Installation ==
- * Install the plugin
-
- {{{
- symfony plugin-install http://plugins.symfony-project.com/sfPropelActAsNestedSetBehaviorPlugin
- }}}
- * Add new fields to your schema.xml
-
- {{{
- #!xml
- <column name="tree_left" type="INTEGER" required="true" />
- <column name="tree_right" type="INTEGER" required="true" />
- <column name="tree_parent" type="INTEGER" required="true" />
- <column name="scope" type="INTEGER" required="true" />
- }}}
- `scope` and `tree_parent` columns can be of any type.
- * 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:
- {{{
- #!php
- <?php
- // lib/model/ForumPost.php
- class ForumPost
- {
- }
- $columns_map = array('left' => ForumPostPeer::TREE_LEFT,
- 'right' => ForumPostPeer::TREE_RIGHT,
- 'parent' => ForumPostPeer::TREE_PARENT,
- 'scope' => ForumPostPeer::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
- <?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
- <?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
- */
- }}}
- === Lame threaded forum posts list example ===
- {{{
- #!php
- <?php $root = ForumPostPeer::retrieveByPk(rootnodepk); ?>
- <ul>
- <?php echo $root->getTitle() ?>
- <?php foreach ($root->getDescendants() as $post): ?>
- <li style="padding-left: <?php echo $post->getLevel() ?>em;">
- <?php echo $post->getTitle() ?>
- </li>
- <?php endforeach; ?>
- </ul>
- }}}
- === Using nested sets and sfPropelPager ===
- {{{
- #!php
- <?php
- // Decide which posts to fetch
- $c = new Criteria();
- $c->add(ForumPostPeer::TOPIC_ID, $topic_id);
- $c->addAscendingOrderByColumn(ForumPostPeer::TREE_LEFT); // ForumPostPeer::TREE_LEFT is the column holding nested set's left value
-
- // Create pager
- $pager = new sfPropelPager('ForumPost', 10);
- $pager->setCriteria($c);
- $pager->setPage($this->getRequestParameter('page', 1));
- $pager->init();
- }}}
- == 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.
- * `void insertAsParentOf(BaseObject $dest_node)` : Inserts node as parent 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.
- * `bool isChildOf(BaseObject $node)` : Returns true if given node is parent of node.
- * `bool isDescendantOf(BaseObject $node)` : Returns true if given node is descendant of node.
- * `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 ===
- * `BaseObject|null getParent($peer_method = 'retrieveByPk')` : Returns node parent or null if node does not have a parent.
- * `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()` : Returns given node first child.
- * `BaseObject retrieveLastChild()` : Returns given node last child.
- * `BaseObject retrieveParent($peer_method = 'doSelectOne')` : Returns given node parent.
- * `array retrieveSiblings()` : Returns node siblings.
- * `array getPath($peer_method = 'doSelectOne')` : Returns path to a specific node as an array, useful to create breadcrumbs.
- === 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(BaseObject $dest_node)` : Moves node to next sibling of given node.
- * `void moveToPrevSiblingOf(BaseObject $dest_node)` : Moves node to previous sibling of given node.
- * `void deleteChildren()` : Deletes node direct children
- * `void deleteDescendants($peer_method = 'doSelect')` : Deletes node descendants (n level)
- === Helper methods ===
- * `void makeRoot()` : Sets node properties to make it a root node.
- * `BaseObject reload()` : Returns an up to date version of node
- * `bool isEqualTo(BaseObject $node)` : Returns true if given node is equivalent to node.
- == Roadmap ==
- === 0.10.0 ===
- ==== Features ====
- * add support for symfony's i18n capabilities
- * add criteria option to more methods
- * add `$peer_method` as an optional parameter to `getParent()` and `getPath()`
- * add multiple connections support
- * add transactional support
- * get rid of mysql dependency : rewrite queries "criteria-like" or implement adapter.
- * add a method to copy a whole tree to another scope
- * make it possible to delete a root node that has children
-
- == Changelog ==
- === 2008-06-07 | Trunk ===
- * feature: Added a backward compatible Criteria parameter in `getChildren()`, `getDescendants()`, `retrieveSiblings()` and `getPath()` (fzaninotto)
- * feature: Added a backward compatible `$peer_method` in `deleteDescendants()` method (works the same way
- as ->getChildren() and ->getDescendants() $peer_method parameter)
- * feature: Allow user to change behaviour name in app.yml (Frederic Coelho)
- === 2007-07-30 | 0.9.1-beta2 ===
- * bugfix: Implemented more reliable `deleteDescendants()` method (Alexander Alexandrov)
- === 2007-07-23 | 0.9.1-beta ===
- ==== Bugfixes ====
- * fixed `getLevel()` cache (gordon franke)
- * fixed scope handling : scope can be any type of data (Jorn.Wagner)
- * `retrieveFirstChild()` and `retrieveLastChild()` missing references to scope node (Olivier.Mansour)
- * fixed postgresql compatibility (Maciej.Filipiak & Krasimir.Angelov)
- * added a note about supported RDBMS (tristan)
- * made roadmap clearer (tristan)
- * removed useless Propel::getConnection (Eric.Fredj)
- * fixed scope handling in `deleteDescendants()` (Piers.Warmers)
- * fixed new `getDescendants()` implementation node level caching (tristan)
- ==== Enhancements ====
- * added new `isDescendantOf()` method (Piers.Warmers)
- * implemented faster getPath() method (francois)
- * implemented faster `getDescendants()` (Jon.Collins)
- === 2007-05-24 | 0.9.0-beta ===
- * Licence change : MIT -> LGPL
- * Please welcome a new maintainer : Gordan Franke :)
- * tree "dumper" utility method : `sfPropelActAsNestedSetBehaviorUtils::dumpTree()`
- * add optional select method for getPath|getParent|retrieveParent (gordon)
- === 2007-04-18 | 0.8.2-beta ===
- * added `getParent()` method (olivier mansour)
- * added `getLevel()` unit tests
- * implemented caching of level in collection retrieval methods : `getDescendants()`, `getChildren()`, `retrieveSiblings()`
- * defined plugin roadmap
- === 2007-03-22 | 0.8.1-beta ===
- * fixed #1480 : non-abstracted column name (paul markovitch)
- * fixed bug in `getStubFromPeer()`
- * `makeRoot()` should accept non new objects (peter van garderen)
- * `getDescendants()` should not try to get descendants if node is a leaf (peter van garderen)
- * updated unit tests
- * enabled syntax highlighting in README
- === 2007-02-19 | 0.8.0-beta ===
- Implemented more methods (+ unit tests) :
- * `insertAsParentOf`
- * `retrieveSiblings`
- * `isEqualTo`
- * `isChildOf`
- Enhanced internal API
- === 2007-02-19 | 0.7.0-beta ===
- Implemented missing methods (+ unit tests) :
-
- * `moveToPrevSiblingOf`
- * `moveToNextSiblingOf`
- * `deleteChildren`
- * `deleteDescendants`
- === 2007-02-19 | 0.6.2-beta ===
- Fixed a bug due to wrong usage of `rtrim`. (Thanks to Krešo Kunjas)
- === 2007-02-15 | 0.6.1-beta ===
- Fixed minor bug in `getPath()`
- === 2007-02-15 | 0.6.0-beta ===
- Implemented missing node retrieval methods :
- * `retrieveFirstChild`
- * `retrieveLastChild`
- * `retrieveParent`
- * `getPath`
-
- Updated docs and unit tests accordingly
- === 2007-02-14 | 0.5.1-beta ===
- Pear package missed plugin's config.php file.
- === 2007-02-14 | 0.5.0-beta ===
- Initial public release. The behavior is stable and fully unit-tested, but the API is not yet complete. Missing methods :
- * `retrieveFirstChild`
- * `retrieveLastChild`
- * `moveToPrevSiblingOf`
- * `moveToNextSiblingOf`
- * `deleteChildren`
- * `deleteTree`
- * `getPath`
- == Maintainers ==
-
- Tristan Rivoallan and Gordon Franke.
- == Contributors ==
- Alexander Alexandrov, Krasimir Angelov, Jon Collins, Maciej Filipiak, Eric Fredj, Peter van Garderen, Olivier Mansour, Paul Markovitch,
- Krešo Kunjas, Piers Warmers, Francois Zaninotto, Romain Dorgueil.
- Plugin's code is based on work from [http://propel.phpdb.org/trac/ticket/312 Heltem]
- and [http://www.symfony-project.com/forum/index.php/m/20657/ Joe Simms].
- Thanks to all of you !
|