1- This is Laravel 4 package that simplifies creating, managing and retrieving trees
2- in database. Using [ Nested Set] ( http://en.wikipedia.org/wiki/Nested_set_model )
3- technique high performance descendants retrieval and path-to-node queries can be done.
1+ Say hi to Laravel 4 extension that will allow to create and manage hierarchies in
2+ your database out-of-box. You can:
43
5- __ IMPORTANT!__ To keep realization of Nested Set Model simple, it's made to work
6- within single HTTP requests. Don't build trees in your code. [ But, it's possible] ( #multiple-node-insertion ) .
4+ * Create multi-level menus and select items of specific level;
5+ * Create categories for the store with no limit of nesting level, query for
6+ descendants and ancestors;
7+ * Forget about performance issues!
78
89## Installation
910
10- The package can be installed as Composer package , just include it into
11- ` required ` section of your ` composer.json ` file:
11+ The package can be installed using Composer, just include it into ` required `
12+ section of your ` composer.json ` file:
1213
13- "required": {
14- "kalnoy/nestedset": "dev-master"
15- }
14+ ``` json
15+ "required" : {
16+ "kalnoy/nestedset" : " dev-master"
17+ }
18+ ```
1619
17- And then hit ` composer update ` in the terminal. That's it - you are ready to go next.
20+ Hit ` composer update ` in the terminal, and you are ready to go next!
1821
1922## Basic usage
2023
2124### Schema
2225
23- Storing trees in database requires additional columns for the table, so these
24- fields need to be included in table schema. We use ` NestedSet::columns($table) `
25- inside table schema creation function, like so:
26+ Storing hierarchies in a database requires additional columns for the table, so these
27+ fields need to be included in the migration. There is a helper for this:
2628
2729``` php
2830<?php
@@ -48,8 +50,9 @@ class CreateCategoriesTable extends Migration {
4850 NestedSet::columns($table);
4951 });
5052
53+ // The root node is required
5154 NestedSet::createRoot('categories', array(
52- 'title' => 'Root ',
55+ 'title' => 'Store ',
5356 ));
5457 }
5558
@@ -65,12 +68,13 @@ class CreateCategoriesTable extends Migration {
6568}
6669```
6770
68- To simplify things root node is required. ` NestedSet::createRoot ` creates it for us.
69-
7071### The model
7172
72- The next step is to create ` Eloquent ` model. Do it whatever way you like, but
73- make shure that model is extended from ` \Kalnoy\Nestedset\Node ` , like here:
73+ The next step is to create ` Eloquent ` model. I prefer [ Jeffrey Way's generators] [ 1 ] ,
74+ but you can stick to whatever you prefer, just make shure that model is extended
75+ from ` \Kalnoy\Nestedset\Node ` , like here:
76+
77+ [ 1 ] : https://github.com/JeffreyWay/Laravel-4-Generators
7478
7579``` php
7680<?php
@@ -80,16 +84,20 @@ class Category extends \Kalnoy\Nestedset\Node {}
8084
8185### Queries
8286
83- You can create nodes like so :
87+ You can insert nodes using several methods :
8488
8589``` php
8690$node = new Category(array('title' => 'TV\'s'));
87- $node->appendTo(Category::root())->save();
91+ $target = Category::root();
92+
93+ $node->appendTo($target)->save();
94+ $node->prependTo($target)->save();
8895```
8996
90- the same thing can be done differently (to allow changing parent via mass assignment) :
97+ The parent can be changed via mass asignment :
9198
9299``` php
100+ // The equivalent of $node->appendTo(Category::find($parent_id))
93101$node->parent_id = $parent_id;
94102$node->save();
95103```
@@ -104,7 +112,7 @@ $srcNode->after($targetNode)->save();
104112$srcNode->before($targetNode)->save();
105113```
106114
107- Path to the node can be obtained in two ways:
115+ _ Ancestors _ can be obtained in two ways:
108116
109117``` php
110118// Target node will not be included into result since it is already available
@@ -118,27 +126,42 @@ or using the scope:
118126$path = Category::pathTo($nodeId)->get();
119127```
120128
121- Descendant nodes can easily be gotten this way:
129+ _ Descendants _ can easily be retrieved in this way:
122130
123131``` php
124132$descendants = $node->descendants()->get();
125133```
126134
127- Nodes can be provided with depth level if scope ` withDepth ` is applied:
135+ This method returns query builder, so you can apply any constraints or eager load
136+ some relations.
137+
138+ There are few more methods:
139+
140+ * ` siblings() ` for querying siblings of the node;
141+ * ` nextSiblings() ` and ` prevSiblings() ` to query nodes after and before the node
142+ respectively.
143+
144+ Nodes can be provided with _ nesting level_ if scope ` withDepth ` is applied:
128145
129146``` php
130147// Each node instance will recieve 'depth' attribute with depth level starting at
131148// zero for the root node.
132149$nodes = Category::withDepth()->get();
133150```
134151
135- Query can be filtered out from the root node using scope ` withoutRoot ` :
152+ Using ` depth ` attribute it is possible to get nodes with maximum level of nesting:
153+
154+ ``` php
155+ $menu = Menu::withDepth()->having('depth', '<=', 2)->get();
156+ ```
157+
158+ The root node can be filtered out using scope ` withoutRoot ` :
136159
137160``` php
138161$nodes = Category::withoutRoot()->get();
139162```
140163
141- Deleting nodes is as simple as before :
164+ Nothing changes when you need to remove the node :
142165
143166``` php
144167$node->delete();
@@ -150,16 +173,48 @@ There are two relations provided by `Node`: _children_ and _parent_.
150173
151174### Insertion, re-insertion and deletion of nodes
152175
153- Operations such as insertion and deletion of nodes imply several independent queries
176+ Operations such as insertion and deletion of nodes imply extra queries
154177before node is actually saved. That is why if something goes wrong, the whole tree
155- might be broken. To avoid such situations each call to ` save() ` must be enclosed
156- into transaction.
178+ might be broken. To avoid such situations, each call of ` save() ` has to be enclosed
179+ in the transaction.
180+
181+ ## How-tos
182+
183+ ### Move node up or down
184+
185+ Sometimes there is need to move nodes around while remaining in boundaries of
186+ the parent.
187+
188+ To move node down, this snippet can be used:
189+
190+ ``` php
191+ if ($sibling = $node->nextSiblings()->first())
192+ {
193+ $node->after($sibling)->save();
194+ }
195+ ```
157196
158- Also, experimentally was noticed that using transaction drastically improves
159- performance when tree gets update.
197+ Moving up is a little bit trickier:
198+
199+ ``` php
200+ if ($sibling = $node->prevSiblings()->reversed()->first())
201+ {
202+ $node->before($sibling)->save();
203+ }
204+ ```
205+
206+ To move node up we need to insert it before node that is right at the top of it.
207+ If we use ` $node->prevSiblings()->first() ` we'll get the first child of the parent
208+ since all nodes are ordered by fixed values. We apply ` reversed() ` scope to reverse
209+ default order.
160210
161211## Advanced usage
162212
213+ ### Default order
214+
215+ Nodes are ordered by lft column unless there is ` limit ` or ` offset ` is provided,
216+ or when user uses ` orderBy ` .
217+
163218### Custom collection
164219
165220This package also provides custom collection, which has two additional functions:
@@ -205,9 +260,10 @@ This is what we are going to get:
205260}];
206261```
207262
208- Even though the query returned all nodes but _ Netbooks_ , the resulting tree does not contain any
209- child from that node. This is very helpful when nodes are soft deleted. Active children of soft
210- deleted nodes will inevitably show up in query results, which is not desired in most situations.
263+ Even though the query returned all nodes but _ Netbooks_ , the resulting tree does
264+ not contain any child from that node. This is very helpful when nodes are soft deleted.
265+ Active children of soft deleted nodes will inevitably show up in query results,
266+ which is not desired in most situations.
211267
212268### Multiple node insertion
213269
@@ -244,48 +300,6 @@ work just fine.
244300_ THIS IS THE ONLY CASE WHEN MULTIPLE NODES CAN BE INSERTED AND/OR RE-INSERTED
245301DURING SINGLE HTTP REQUEST WITHOUT REFRESHING DATA_
246302
247- #### If you still need this
248-
249- If you are up to create your tree structure in your code, make shure that target node
250- is always updated. Here is the description of what nodes are target when using insertion
251- functions:
252-
253- ``` php
254- /**
255- * @var Category $node The node being inserted
256- * @var Category $target The target node
257- */
258-
259- $node->appendTo($target);
260- $node->prependTo($target);
261- $node->before($target);
262- $node->after($target);
263- $target->append($node);
264- $target->prepend($node);
265- ```
266-
267- When doing multiple insertions, just call ` $target->refresh() ` each time before calling
268- any of the above functions.
269-
270- ``` php
271- DB::transaction(function () {
272- $node = new Category(...);
273- $root = Category::root();
274-
275- // The root here is updated automatically
276- $node->appendTo($root)->save();
277-
278- $nodeSubNode = new Category(...);
279- // No need to update $node since it is just saved
280- // Also, $node gets update since it is new parent for $nodeSubNode
281- $nodeSubNode->appendTo($node)->save();
282-
283- $nodeSibling = new Category(...);
284- // We refresh $root because it is not updated since last operation
285- $nodeSibling->appendTo($root->refresh())->save();
286- });
287- ```
288-
289303### Deleting nodes
290304
291305To delete a node, you just call ` $node->delete() ` as usual. If node is soft deleted,
@@ -296,4 +310,8 @@ When you create your table's schema and use `NestedSet::columns`, it creates for
296310key for you, since nodes are connected by ` parent_id ` attribute. When you hard delete
297311the node, all of descendants are cascaded.
298312
299- In case when DBMS doesn't support foreign keys, descendants are removed manually.
313+ In case when DBMS doesn't support foreign keys, descendants are still removed.
314+
315+ ## TODO
316+
317+ [ * ] Build up hierarchy from array;
0 commit comments