123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- = !NestedSet support in Propel =
- '''Warning''': Since Propel 1.5, the support for nested sets was moved to the `nested_set` behavior. The method described here is deprecated.'''
- == Description ==
- With !NestedSet implementation, trees are stored using different approach in databases: [http://www.sitepoint.com/article/hierarchical-data-database]
- Nested Set implementation requires three dedicated fields in table structure
- * left
- * right
- Plus an optional fields for multi nested set support
- * scope
- ''NB: fields name are free and must be defined in schema.xml''
- To enable !NestedSet support in table, schema.xml must define some specific attributes:
- '''treeMode''' which must take the value '''!NestedSet'''
- {{{
- #!xml
- <table name="menu" idMethod="native" treeMode="NestedSet">
- }}}
- Then, left and right field must be defined that way '''nestedSetLeftKey''' as a boolean value as '''nestedSetRightKey'''
- {{{
- #!xml
- <column name="lft" type="INTEGER" required="true" default="0" nestedSetLeftKey="true"/>
- <column name="rgt" type="INTEGER" required="true" default="0" nestedSetRightKey="true"/>
- }}}
- For multi nestedset support, an other column must be defined with boolean attribute '''treeScopeKey''' set to true
- {{{
- #!xml
- <column name="scope" type="INTEGER" required="true" default="0" treeScopeKey="true"/>
- }}}
- And then, let's the propel generator automagically create all the needed model and stub classes.
- == !NestedSet usage in Propel ==
- ''ex:''
- '''schema.xml''' extract
- {{{
- #!xml
- <table name="menu" idMethod="native" treeMode="NestedSet">
- <column name="id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true"/>
- <column name="lft" type="INTEGER" required="true" default="0" nestedSetLeftKey="true"/>
- <column name="rgt" type="INTEGER" required="true" default="0" nestedSetRightKey="true"/>
- <column name="scope" type="INTEGER" required="true" default="0" treeScopeKey="true"/>
- <column name="text" type="VARCHAR" size="128" required="true" default=""/>
- <column name="link" type="VARCHAR" size="255" required="true" default=""/>
- <index name="lft">
- <index-column name="lft"/>
- </index>
- <index name="rgt">
- <index-column name="rgt"/>
- </index>
- <index name="scope">
- <index-column name="scope"/>
- </index>
- </table>
- }}}
- === !NestedSet insertion ===
- {{{
- #!php
- <?php
- $root = new Menu();
- $root->setText('Google');
- $root->setLink('http://www.google.com');
- $root->makeRoot();
- $root->save();
- $menu = new Menu();
- $menu->setText('Google Mail');
- $menu->setLink('http://mail.google.com');
- $menu->insertAsLastChildOf($root);
- $menu->save();
- $child = new Menu();
- $child->setText('Google Maps');
- $child->setLink('http://maps.google.com');
- $child->insertAsLastChildOf($root);
- $child->save();
- $sibling = new Menu();
- $sibling->setText('Yahoo!');
- $sibling->setLink('http://www.yahoo.com');
- $sibling->insertAsNextSiblingOf($root);
- $sibling->save();
- $child = new Menu();
- $child->setText('Yahoo! Mail');
- $child->setLink('http://mail.yahoo.com');
- $child->insertAsLastChildOf($sibling);
- $child->save();
- }}}
- === Multi !NestedSet insertion ===
- {{{
- #!php
- <?php
- // Create first root node
- $root = new Menu();
- $root->setText('Google');
- $root->setLink('http://www.google.com');
- $root->makeRoot();
- $root->setScopeIdValue(1); // Tree 1
- $root->save();
- $menu = new Menu();
- $menu->setText('Google Mail');
- $menu->setLink('http://mail.google.com');
- $menu->insertAsLastChildOf($root);
- $menu->save();
- // Create secund root node
- $root2 = new Menu();
- $root2->setText('Yahoo!');
- $root2->setLink('http://www.yahoo.com');
- $root2->makeRoot();
- $root2->setScopeIdValue(2); // Tree 2
- $root2->save();
- $menu = new Menu();
- $menu->setText('Yahoo! Mail');
- $menu->setLink('http://mail.yahoo.com');
- $menu->insertAsLastChildOf($root2);
- $menu->save();
- }}}
- === Tree retrieval ===
- {{{
- #!php
- <?php
- class myMenuOutput extends RecursiveIteratorIterator {
- function __construct(Menu $m) {
- parent::__construct($m, self::SELF_FIRST);
- }
- function beginChildren() {
- echo str_repeat("\t", $this->getDepth());
- }
- function endChildren() {
- echo str_repeat("\t", $this->getDepth() - 1);
- }
- }
- $menu = MenuPeer::retrieveTree($scopeId);
- $it = new myMenuOutput($menu);
- foreach($it as $m) {
- echo $m->getText(), '[', $m->getLeftValue(), '-', $m->getRightValue(), "]\n";
- }
- }}}
- === Tree traversal ===
- !NestetSet implementation use the [http://somabo.de/talks/200504_php_quebec_spl_for_the_masses.pdf SPL RecursiveIterator] as suggested by soenke
- == !NestedSet known broken behaviour ==
- === Issue description ===
- For every changes applied on the tree, several entries in the database can be involved. So all already loaded nodes have to be refreshed with their new left/right values.
- === InstancePool enabled ===
- In order to refresh all loaded nodes, an automatic internal call is made after each tree change to retrieve all instance in InstancePool and update them.
- And it works fine.
- === InstancePool disabled ===
- When InstancePool is disabled, their is no way to retrieve references to all already loaded node and get them updated.
- So in most case, all loaded nodes are not updated and it leads to an inconsistency state.
- So, workaround is to do an explicit reload for any node you use after tree change.
|