Nested-Set.txt 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. = !NestedSet support in Propel =
  2. '''Warning''': Since Propel 1.5, the support for nested sets was moved to the `nested_set` behavior. The method described here is deprecated.'''
  3. == Description ==
  4. With !NestedSet implementation, trees are stored using different approach in databases: [http://www.sitepoint.com/article/hierarchical-data-database]
  5. Nested Set implementation requires three dedicated fields in table structure
  6. * left
  7. * right
  8. Plus an optional fields for multi nested set support
  9. * scope
  10. ''NB: fields name are free and must be defined in schema.xml''
  11. To enable !NestedSet support in table, schema.xml must define some specific attributes:
  12. '''treeMode''' which must take the value '''!NestedSet'''
  13. {{{
  14. #!xml
  15. <table name="menu" idMethod="native" treeMode="NestedSet">
  16. }}}
  17. Then, left and right field must be defined that way '''nestedSetLeftKey''' as a boolean value as '''nestedSetRightKey'''
  18. {{{
  19. #!xml
  20. <column name="lft" type="INTEGER" required="true" default="0" nestedSetLeftKey="true"/>
  21. <column name="rgt" type="INTEGER" required="true" default="0" nestedSetRightKey="true"/>
  22. }}}
  23. For multi nestedset support, an other column must be defined with boolean attribute '''treeScopeKey''' set to true
  24. {{{
  25. #!xml
  26. <column name="scope" type="INTEGER" required="true" default="0" treeScopeKey="true"/>
  27. }}}
  28. And then, let's the propel generator automagically create all the needed model and stub classes.
  29. == !NestedSet usage in Propel ==
  30. ''ex:''
  31. '''schema.xml''' extract
  32. {{{
  33. #!xml
  34. <table name="menu" idMethod="native" treeMode="NestedSet">
  35. <column name="id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true"/>
  36. <column name="lft" type="INTEGER" required="true" default="0" nestedSetLeftKey="true"/>
  37. <column name="rgt" type="INTEGER" required="true" default="0" nestedSetRightKey="true"/>
  38. <column name="scope" type="INTEGER" required="true" default="0" treeScopeKey="true"/>
  39. <column name="text" type="VARCHAR" size="128" required="true" default=""/>
  40. <column name="link" type="VARCHAR" size="255" required="true" default=""/>
  41. <index name="lft">
  42. <index-column name="lft"/>
  43. </index>
  44. <index name="rgt">
  45. <index-column name="rgt"/>
  46. </index>
  47. <index name="scope">
  48. <index-column name="scope"/>
  49. </index>
  50. </table>
  51. }}}
  52. === !NestedSet insertion ===
  53. {{{
  54. #!php
  55. <?php
  56. $root = new Menu();
  57. $root->setText('Google');
  58. $root->setLink('http://www.google.com');
  59. $root->makeRoot();
  60. $root->save();
  61. $menu = new Menu();
  62. $menu->setText('Google Mail');
  63. $menu->setLink('http://mail.google.com');
  64. $menu->insertAsLastChildOf($root);
  65. $menu->save();
  66. $child = new Menu();
  67. $child->setText('Google Maps');
  68. $child->setLink('http://maps.google.com');
  69. $child->insertAsLastChildOf($root);
  70. $child->save();
  71. $sibling = new Menu();
  72. $sibling->setText('Yahoo!');
  73. $sibling->setLink('http://www.yahoo.com');
  74. $sibling->insertAsNextSiblingOf($root);
  75. $sibling->save();
  76. $child = new Menu();
  77. $child->setText('Yahoo! Mail');
  78. $child->setLink('http://mail.yahoo.com');
  79. $child->insertAsLastChildOf($sibling);
  80. $child->save();
  81. }}}
  82. === Multi !NestedSet insertion ===
  83. {{{
  84. #!php
  85. <?php
  86. // Create first root node
  87. $root = new Menu();
  88. $root->setText('Google');
  89. $root->setLink('http://www.google.com');
  90. $root->makeRoot();
  91. $root->setScopeIdValue(1); // Tree 1
  92. $root->save();
  93. $menu = new Menu();
  94. $menu->setText('Google Mail');
  95. $menu->setLink('http://mail.google.com');
  96. $menu->insertAsLastChildOf($root);
  97. $menu->save();
  98. // Create secund root node
  99. $root2 = new Menu();
  100. $root2->setText('Yahoo!');
  101. $root2->setLink('http://www.yahoo.com');
  102. $root2->makeRoot();
  103. $root2->setScopeIdValue(2); // Tree 2
  104. $root2->save();
  105. $menu = new Menu();
  106. $menu->setText('Yahoo! Mail');
  107. $menu->setLink('http://mail.yahoo.com');
  108. $menu->insertAsLastChildOf($root2);
  109. $menu->save();
  110. }}}
  111. === Tree retrieval ===
  112. {{{
  113. #!php
  114. <?php
  115. class myMenuOutput extends RecursiveIteratorIterator {
  116. function __construct(Menu $m) {
  117. parent::__construct($m, self::SELF_FIRST);
  118. }
  119. function beginChildren() {
  120. echo str_repeat("\t", $this->getDepth());
  121. }
  122. function endChildren() {
  123. echo str_repeat("\t", $this->getDepth() - 1);
  124. }
  125. }
  126. $menu = MenuPeer::retrieveTree($scopeId);
  127. $it = new myMenuOutput($menu);
  128. foreach($it as $m) {
  129. echo $m->getText(), '[', $m->getLeftValue(), '-', $m->getRightValue(), "]\n";
  130. }
  131. }}}
  132. === Tree traversal ===
  133. !NestetSet implementation use the [http://somabo.de/talks/200504_php_quebec_spl_for_the_masses.pdf SPL RecursiveIterator] as suggested by soenke
  134. == !NestedSet known broken behaviour ==
  135. === Issue description ===
  136. 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.
  137. === InstancePool enabled ===
  138. 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.
  139. And it works fine.
  140. === InstancePool disabled ===
  141. When InstancePool is disabled, their is no way to retrieve references to all already loaded node and get them updated.
  142. So in most case, all loaded nodes are not updated and it leads to an inconsistency state.
  143. So, workaround is to do an explicit reload for any node you use after tree change.