PageRenderTime 51ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/documentation/behaviors/sortable.markdown

http://github.com/propelorm/Propel2
Markdown | 273 lines | 224 code | 49 blank | 0 comment | 0 complexity | bf39466405e67fde2618fa6860dd1eb3 MD5 | raw file
  1. ---
  2. layout: documentation
  3. title: Sortable Behavior
  4. ---
  5. # Sortable Behavior #
  6. The `sortable` behavior allows a model to become an ordered list, and provides numerous methods to traverse this list in an efficient way.
  7. ## Basic Usage ##
  8. In the `schema.xml`, use the `<behavior>` tag to add the `sortable` behavior to a table:
  9. {% highlight xml %}
  10. <table name="task">
  11. <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
  12. <column name="title" type="VARCHAR" required="true" primaryString="true" />
  13. <behavior name="sortable" />
  14. </table>
  15. {% endhighlight %}
  16. Rebuild your model, insert the table creation sql again, and you're ready to go. The model now has the ability to be inserted into an ordered list, as follows:
  17. {% highlight php %}
  18. <?php
  19. $t1 = new Task();
  20. $t1->setTitle('Wash the dishes');
  21. $t1->save();
  22. echo $t1->getRank(); // 1, the first rank to be given (not 0)
  23. $t2 = new Task();
  24. $t2->setTitle('Do the laundry');
  25. $t2->save();
  26. echo $t2->getRank(); // 2
  27. $t3 = new Task();
  28. $t3->setTitle('Rest a little');
  29. $t3->save()
  30. echo $t3->getRank(); // 3
  31. {% endhighlight %}
  32. As long as you save new objects, Propel gives them the first available rank in the list.
  33. Once you have built an ordered list, you can traverse it using any of the methods added by the `sortable` behavior. For instance:
  34. {% highlight php %}
  35. <?php
  36. $firstTask = TaskQuery::create()->findOneByRank(1); // $t1
  37. $secondTask = $firstTask->getNext(); // $t2
  38. $lastTask = $secondTask->getNext(); // $t3
  39. $secondTask = $lastTask->getPrevious(); // $t2
  40. $allTasks = TaskQuery::create()->findList();
  41. // => collection($t1, $t2, $t3)
  42. $allTasksInReverseOrder = TaskQuery::create()->orderByRank('desc')->find();
  43. // => collection($t3, $t2, $t2)
  44. {% endhighlight %}
  45. The results returned by these methods are regular Propel model objects, with access to the properties and related models. The `sortable` behavior also adds inspection methods to objects:
  46. {% highlight php %}
  47. <?php
  48. echo $t2->isFirst(); // false
  49. echo $t2->isLast(); // false
  50. echo $t2->getRank(); // 2
  51. {% endhighlight %}
  52. ## Manipulating Objects In A List ##
  53. You can move an object in the list using any of the `moveUp()`, `moveDown()`, `moveToTop()`, `moveToBottom()`, `moveToRank()`, and `swapWith()` methods. These operations are immediate and don't require that you save the model afterwards:
  54. {% highlight php %}
  55. <?php
  56. // The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
  57. $t2->moveToTop();
  58. // The list is now 1 - Do the laundry, 2 - Wash the dishes, 3 - Rest a little
  59. $t2->moveToBottom();
  60. // The list is now 1 - Wash the dishes, 2 - Rest a little, 3 - Do the laundry
  61. $t2->moveUp();
  62. // The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
  63. $t2->swapWith($t1);
  64. // The list is now 1 - Do the laundry, 2 - Wash the dishes, 3 - Rest a little
  65. $t2->moveToRank(3);
  66. // The list is now 1 - Wash the dishes, 2 - Rest a little, 3 - Do the laundry
  67. $t2->moveToRank(2);
  68. {% endhighlight %}
  69. By default, new objects are added at the bottom of the list. But you can also insert them at a specific position, using any of the `insertAtTop()`, `insertAtBottom()`, and `insertAtRank()` methods. Note that the `insertAtXXX` methods don't save the object:
  70. {% highlight php %}
  71. <?php
  72. // The list is 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
  73. $t4 = new Task();
  74. $t4->setTitle('Clean windows');
  75. $t4->insertAtRank(2);
  76. $t4->save();
  77. // The list is now 1 - Wash the dishes, 2 - Clean Windows, 3 - Do the laundry, 4 - Rest a little
  78. {% endhighlight %}
  79. Whenever you `delete()` an object, the ranks are rearranged to fill the gap:
  80. {% highlight php %}
  81. <?php
  82. $t4->delete();
  83. // The list is now 1 - Wash the dishes, 2 - Do the laundry, 3 - Rest a little
  84. {% endhighlight %}
  85. >**Tip**<br />You can remove an object from the list without necessarily deleting it by calling `removeFromList()`. Don't forget to `save()` it afterwards so that the other objects in the lists are rearranged to fill the gap.
  86. ## Multiple Lists ##
  87. When you need to store several lists for a single model - for instance, one task list for each user - use a _scope_ for each list. This requires that you enable scope support in the behavior definition by setting the `use_scope` parameter to `true`:
  88. {% highlight xml %}
  89. <table name="task">
  90. <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
  91. <column name="title" type="VARCHAR" required="true" primaryString="true" />
  92. <column name="user_id" required="true" type="INTEGER" />
  93. <foreign-key foreignTable="user" onDelete="cascade">
  94. <reference local="user_id" foreign="id" />
  95. </foreign-key>
  96. <behavior name="sortable">
  97. <parameter name="use_scope" value="true" />
  98. <parameter name="scope_column" value="user_id" />
  99. </behavior>
  100. </table>
  101. {% endhighlight %}
  102. Now, after rebuilding your model, you can have as many lists as required:
  103. {% highlight php %}
  104. <?php
  105. // test users
  106. $paul = new User();
  107. $john = new User();
  108. // now onto the tasks
  109. $t1 = new Task();
  110. $t1->setTitle('Wash the dishes');
  111. $t1->setUser($paul);
  112. $t1->save();
  113. echo $t1->getRank(); // 1
  114. $t2 = new Task();
  115. $t2->setTitle('Do the laundry');
  116. $t2->setUser($paul);
  117. $t2->save();
  118. echo $t2->getRank(); // 2
  119. $t3 = new Task();
  120. $t3->setTitle('Rest a little');
  121. $t3->setUser($john);
  122. $t3->save()
  123. echo $t3->getRank(); // 1, because John has his own task list
  124. {% endhighlight %}
  125. The generated methods now accept a `$scope` parameter to restrict the query to a given scope:
  126. {% highlight php %}
  127. <?php
  128. $firstPaulTask = TaskQuery::create()->findOneByRank($rank = 1, $scope = $paul->getId()); // $t1
  129. $lastPaulTask = $firstTask->getNext(); // $t2
  130. $firstJohnTask = TaskPeer::create()->findOneByRank($rank = 1, $scope = $john->getId()); // $t1
  131. {% endhighlight %}
  132. Models using the sortable behavior with scope benefit from one additional Query methods named `inList()`:
  133. {% highlight php %}
  134. <?php
  135. $allPaulsTasks = TaskPeer::create()->inList($scope = $paul->getId())->find();
  136. {% endhighlight %}
  137. ## Parameters ##
  138. By default, the behavior adds one columns to the model - two if you use the scope feature. If these columns are already described in the schema, the behavior detects it and doesn't add them a second time. The behavior parameters allow you to use custom names for the sortable columns. The following schema illustrates a complete customization of the behavior:
  139. {% highlight xml %}
  140. <table name="task">
  141. <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" />
  142. <column name="title" type="VARCHAR" required="true" primaryString="true" />
  143. <column name="my_rank_column" required="true" type="INTEGER" />
  144. <column name="user_id" required="true" type="INTEGER" />
  145. <foreign-key foreignTable="user" onDelete="cascade">
  146. <reference local="user_id" foreign="id" />
  147. </foreign-key>
  148. <behavior name="sortable">
  149. <parameter name="rank_column" value="my_rank_column" />
  150. <parameter name="use_scope" value="true" />
  151. <parameter name="scope_column" value="user_id" />
  152. </behavior>
  153. </table>
  154. {% endhighlight %}
  155. Whatever name you give to your columns, the `sortable` behavior always adds the following proxy methods, which are mapped to the correct column:
  156. {% highlight php %}
  157. <?php
  158. $task->getRank(); // returns $task->my_rank_column
  159. $task->setRank($rank);
  160. $task->getScopeValue(); // returns $task->user_id
  161. $task->setScopeValue($scope);
  162. {% endhighlight %}
  163. The same happens for the generated Query object:
  164. {% highlight php %}
  165. <?php
  166. $query = TaskQuery::create()->filterByRank(); // proxies to filterByMyRankColumn()
  167. $query = TaskQuery::create()->orderByRank(); // proxies to orderByMyRankColumn()
  168. $tasks = TaskQuery::create()->findOneByRank(); // proxies to findOneByMyRankColumn()
  169. {% endhighlight %}
  170. >**Tip**<br />The behavior adds columns but no index. Depending on your table structure, you might want to add a column index by hand to speed up queries on sorted lists.
  171. ## Complete API ##
  172. Here is a list of the methods added by the behavior to the model objects:
  173. {% highlight php %}
  174. <?php
  175. // storage columns accessors
  176. int getRank()
  177. $object setRank(int $rank)
  178. // only for behavior with use_scope
  179. int getScopeValue()
  180. $object setScopeValue(int $scope)
  181. // inspection methods
  182. bool isFirst()
  183. bool isLast()
  184. // list traversal methods
  185. $object getNext()
  186. $object getPrevious()
  187. // methods to insert an object in the list (require calling save() afterwards)
  188. $object insertAtRank($rank)
  189. $object insertAtBottom()
  190. $object insertAtTop()
  191. // methods to move an object in the list (immediate, no need to save() afterwards)
  192. $object moveToRank($rank)
  193. $object moveUp()
  194. $object moveDown()
  195. $object moveToTop()
  196. $object moveToBottom()
  197. $object swapWith($object)
  198. // method to remove an object from the list (requires calling save() afterwards)
  199. $object removeFromList()
  200. {% endhighlight %}
  201. Here is a list of the methods added by the behavior to the query objects:
  202. {% highlight php %}
  203. <?php
  204. query filterByRank($order, $scope = null)
  205. query orderByRank($order, $scope = null)
  206. $object findOneByRank($rank, $scope = null)
  207. coll findList($scope = null)
  208. int getMaxRank($scope = null)
  209. bool reorder($newOrder) // $newOrder is a $id => $rank associative array
  210. // only for behavior with use_scope
  211. array inList($scope)
  212. {% endhighlight %}
  213. The behavior also adds a few methods to the Peer classes:
  214. {% highlight php %}
  215. <?php
  216. int getMaxRank($scope = null)
  217. $object retrieveByRank($rank, $scope = null)
  218. array doSelectOrderByRank($order, $scope = null)
  219. bool reorder($newOrder) // $newOrder is a $id => $rank associative array
  220. // only for behavior with use_scope
  221. array retrieveList($scope)
  222. int countList($scope)
  223. int deleteList($scope)
  224. {% endhighlight %}