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:
4
3
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!
7
8
8
9
## Installation
9
10
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:
12
13
13
- "required": {
14
- "kalnoy/nestedset": "dev-master"
15
- }
14
+ ``` json
15
+ "required" : {
16
+ "kalnoy/nestedset" : " dev-master"
17
+ }
18
+ ```
16
19
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!
18
21
19
22
## Basic usage
20
23
21
24
### Schema
22
25
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:
26
28
27
29
``` php
28
30
<?php
@@ -48,8 +50,9 @@ class CreateCategoriesTable extends Migration {
48
50
NestedSet::columns($table);
49
51
});
50
52
53
+ // The root node is required
51
54
NestedSet::createRoot('categories', array(
52
- 'title' => 'Root ',
55
+ 'title' => 'Store ',
53
56
));
54
57
}
55
58
@@ -65,12 +68,13 @@ class CreateCategoriesTable extends Migration {
65
68
}
66
69
```
67
70
68
- To simplify things root node is required. ` NestedSet::createRoot ` creates it for us.
69
-
70
71
### The model
71
72
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
74
78
75
79
``` php
76
80
<?php
@@ -80,16 +84,20 @@ class Category extends \Kalnoy\Nestedset\Node {}
80
84
81
85
### Queries
82
86
83
- You can create nodes like so :
87
+ You can insert nodes using several methods :
84
88
85
89
``` php
86
90
$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();
88
95
```
89
96
90
- the same thing can be done differently (to allow changing parent via mass assignment) :
97
+ The parent can be changed via mass asignment :
91
98
92
99
``` php
100
+ // The equivalent of $node->appendTo(Category::find($parent_id))
93
101
$node->parent_id = $parent_id;
94
102
$node->save();
95
103
```
@@ -104,7 +112,7 @@ $srcNode->after($targetNode)->save();
104
112
$srcNode->before($targetNode)->save();
105
113
```
106
114
107
- Path to the node can be obtained in two ways:
115
+ _ Ancestors _ can be obtained in two ways:
108
116
109
117
``` php
110
118
// Target node will not be included into result since it is already available
@@ -118,27 +126,42 @@ or using the scope:
118
126
$path = Category::pathTo($nodeId)->get();
119
127
```
120
128
121
- Descendant nodes can easily be gotten this way:
129
+ _ Descendants _ can easily be retrieved in this way:
122
130
123
131
``` php
124
132
$descendants = $node->descendants()->get();
125
133
```
126
134
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:
128
145
129
146
``` php
130
147
// Each node instance will recieve 'depth' attribute with depth level starting at
131
148
// zero for the root node.
132
149
$nodes = Category::withDepth()->get();
133
150
```
134
151
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 ` :
136
159
137
160
``` php
138
161
$nodes = Category::withoutRoot()->get();
139
162
```
140
163
141
- Deleting nodes is as simple as before :
164
+ Nothing changes when you need to remove the node :
142
165
143
166
``` php
144
167
$node->delete();
@@ -150,16 +173,48 @@ There are two relations provided by `Node`: _children_ and _parent_.
150
173
151
174
### Insertion, re-insertion and deletion of nodes
152
175
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
154
177
before 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
+ ```
157
196
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.
160
210
161
211
## Advanced usage
162
212
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
+
163
218
### Custom collection
164
219
165
220
This package also provides custom collection, which has two additional functions:
@@ -205,9 +260,10 @@ This is what we are going to get:
205
260
}];
206
261
```
207
262
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.
211
267
212
268
### Multiple node insertion
213
269
@@ -244,48 +300,6 @@ work just fine.
244
300
_ THIS IS THE ONLY CASE WHEN MULTIPLE NODES CAN BE INSERTED AND/OR RE-INSERTED
245
301
DURING SINGLE HTTP REQUEST WITHOUT REFRESHING DATA_
246
302
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
-
289
303
### Deleting nodes
290
304
291
305
To 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
296
310
key for you, since nodes are connected by ` parent_id ` attribute. When you hard delete
297
311
the node, all of descendants are cascaded.
298
312
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