@@ -68,7 +68,7 @@ Another important note is that __structural manipulations are deferred__ until y
68
68
hit ` save ` on model (some methods implicitly call ` save ` and return boolean result
69
69
of the operation).
70
70
71
- If model is successfully saved it doesn't mean that node has moved. If your application
71
+ If model is successfully saved it doesn't mean that node was moved. If your application
72
72
depends on whether the node has actually changed its position, use ` hasMoved ` method:
73
73
74
74
``` php
@@ -77,12 +77,17 @@ if ($node->save()) {
77
77
}
78
78
```
79
79
80
- #### Creating a new node
80
+ #### Creating nodes
81
81
82
- When you just create a node, it will be appended to the end of the tree:
82
+ When you simply creating a node, it will be appended to the end of the tree:
83
83
84
84
``` php
85
- Category::create($attributes);
85
+ Category::create($attributes); // Saved as root
86
+ ```
87
+
88
+ ``` php
89
+ $node = new Category($attributes);
90
+ $node->save(); // Saved as root
86
91
```
87
92
88
93
In this case the node is considered a _ root_ which means that it doesn't have a parent.
@@ -103,7 +108,7 @@ The node will be appended to the end of the tree.
103
108
104
109
If you want to make node a child of other node, you can make it last or first child.
105
110
106
- In following examples, ` $parent ` is some existing node.
111
+ * In following examples, ` $parent ` is some existing node.*
107
112
108
113
There are few ways to append a node:
109
114
@@ -142,8 +147,8 @@ $parent->prependNode($node);
142
147
143
148
You can make ` $node ` to be a neighbor of the ` $neighbor ` node using following methods:
144
149
145
- * Neighbor is existing node , target node can be fresh. If target node exists,
146
- it will be moved to the new position and parent will be changed if required.*
150
+ * ` $neighbor ` must exists , target node can be fresh. If target node exists,
151
+ it will be moved to the new position and parent will be changed if it's required.*
147
152
148
153
``` php
149
154
# Explicit save
@@ -155,20 +160,6 @@ $node->insertAfterNode($neighbor);
155
160
$node->insertBeforeNode($neighbor);
156
161
```
157
162
158
- #### Shifting a node
159
-
160
- To shift node up or down inside parent:
161
-
162
- ``` php
163
- $bool = $node->down();
164
- $bool = $node->up();
165
-
166
- // Shift node by 3 siblings
167
- $bool = $node->down(3);
168
- ```
169
-
170
- The result of the operation is boolean value of whether the node has changed the position.
171
-
172
163
#### Building a tree from array
173
164
174
165
When using static method ` create ` on node, it checks whether attributes contains
@@ -194,7 +185,7 @@ $node = Category::create([
194
185
195
186
### Retrieving nodes
196
187
197
- In some cases we will use an ` $id ` variable which is an id of the target node.
188
+ * In some cases we will use an ` $id ` variable which is an id of the target node.*
198
189
199
190
#### Ancestors
200
191
@@ -205,20 +196,20 @@ $result = $node->getAncestors();
205
196
// #2 Using a query
206
197
$result = $node->ancestors()->get();
207
198
208
- // #3 Getting ancestors by id of the node
199
+ // #3 Getting ancestors by primary key
209
200
$result = Category::ancestorsOf($id);
210
201
```
211
202
212
203
#### Descendants
213
204
214
205
``` php
215
- // #1 Using accessor
216
- $result = $node->getDescendants() ;
206
+ // #1 Using relationship
207
+ $result = $node->descendants ;
217
208
218
- // #2 Using a query
209
+ // #2 Using a query
219
210
$result = $node->descendants()->get();
220
211
221
- // #3 Getting ancestors by id of the node
212
+ // #3 Getting descendants by primary key
222
213
$result = Category::descendantsOf($id);
223
214
```
224
215
@@ -230,7 +221,7 @@ $result = $node->getSiblings();
230
221
$result = $node->siblings()->get();
231
222
```
232
223
233
- To get just next siblings ( [ default order ] ( #default-order ) is applied here) :
224
+ To get only next siblings:
234
225
235
226
``` php
236
227
// Get a sibling that is immediately after the node
@@ -243,7 +234,7 @@ $result = $node->getNextSiblings();
243
234
$result = $node->nextSiblings()->get();
244
235
```
245
236
246
- To get previous siblings ( [ reversed order ] ( #default-order ) is applied) :
237
+ To get previous siblings:
247
238
248
239
``` php
249
240
// Get a sibling that is immediately before the node
@@ -272,51 +263,6 @@ $categories[] = $category->getKey();
272
263
$goods = Goods::whereIn('category_id', $categories)->get();
273
264
```
274
265
275
- ### Deleting nodes
276
-
277
- To delete a node:
278
-
279
- ``` php
280
- $node->delete();
281
- ```
282
-
283
- ** IMPORTANT!** Any descendant that node has will also be deleted!
284
-
285
- ** IMPORTANT!** Nodes are required to be deleted as models, ** don't** try do delete them using a query like so:
286
-
287
- ``` php
288
- Category::where('id', '=', $id)->delete();
289
- ```
290
-
291
- This will brake the tree!
292
-
293
- ` SoftDeletes ` trait is supported, also on model level.
294
-
295
- ### Collection extension
296
-
297
- This package provides few helpful methods for collection of nodes. You can link nodes in plain collection like so:
298
-
299
- ``` php
300
- $results = Categories::get();
301
-
302
- $results->linkNodes();
303
- ```
304
-
305
- This will fill ` parent ` and ` children ` relations on every node so you can iterate over them without extra database
306
- requests.
307
-
308
- To convert plain collection to tree:
309
-
310
- ```
311
- $tree = $results->toTree();
312
- ```
313
-
314
- ` $tree ` will contain only root nodes and to access children you can use ` children ` relation.
315
-
316
- ### Query builder extension
317
-
318
- This packages extends default query builder to introduce few helpful features.
319
-
320
266
#### Including node depth
321
267
322
268
If you need to know at which level the node is:
@@ -352,13 +298,26 @@ You can get nodes in reversed order:
352
298
$result = Category::reversed()->get();
353
299
```
354
300
301
+ ##### Shifting a node
302
+
303
+ To shift node up or down inside parent to affect default order:
304
+
305
+ ``` php
306
+ $bool = $node->down();
307
+ $bool = $node->up();
308
+
309
+ // Shift node by 3 siblings
310
+ $bool = $node->down(3);
311
+ ```
312
+
313
+ The result of the operation is boolean value of whether the node has changed its
314
+ position.
315
+
355
316
#### Constraints
356
317
357
318
Various constraints that can be applied to the query builder:
358
319
359
320
- __ whereIsRoot()__ to get only root nodes;
360
- - __ hasChildren()__ to get nodes that have children;
361
- - __ hasParent()__ to get non-root nodes;
362
321
- __ whereIsAfter($id)__ to get every node (not just siblings) that are after a node
363
322
with specified id;
364
323
- __ whereIsBefore($id)__ to get every node that is before a node with specified id.
@@ -380,21 +339,88 @@ $result = Category::whereAncestorOf($node)->get();
380
339
381
340
` $node ` can be either a primary key of the model or model instance.
382
341
383
- ### Node methods
342
+ #### Building a tree
343
+
344
+ After getting a set of nodes, you can convert it to tree. For example:
345
+
346
+ ``` php
347
+ $tree = Category::get()->toTree();
348
+ ```
349
+
350
+ This will fill ` parent ` and ` children ` relationships on every node in the set and
351
+ you can render a tree using recursive algorithm:
352
+
353
+ ``` php
354
+ $nodes = Category::get()->toTree();
355
+
356
+ $traverse = function ($categories, $prefix = '-') use (& $traverse) {
357
+ foreach ($categories as $category) {
358
+ echo PHP_EOL.$prefix.' '.$category->name;
359
+
360
+ $traverse($category->children, $prefix.'-');
361
+ }
362
+ };
363
+
364
+ $traverse($nodes);
365
+ ```
366
+
367
+ This will output something like this:
368
+
369
+ ```
370
+ - Root
371
+ -- Child 1
372
+ --- Sub child 1
373
+ -- Child 2
374
+ - Another root
375
+ ```
376
+
377
+ ##### Getting subtree
378
+
379
+ Sometimes you don't need whole tree to be loaded and just some subtree of specific node.
380
+ It is show in following example:
381
+
382
+ ``` php
383
+ $root = Category::find($rootId);
384
+ $tree = $root->descendants->toTree($root);
385
+ ```
386
+
387
+ Now ` $tree ` contains children of ` $root ` node.
388
+
389
+ If you don't need ` $root ` node itself, do following instead:
390
+
391
+ ``` php
392
+ $tree = Category::descendantsOf($rootId)->toTree($rootId);
393
+ ```
394
+
395
+ ### Deleting nodes
396
+
397
+ To delete a node:
398
+
399
+ ``` php
400
+ $node->delete();
401
+ ```
384
402
385
- Compute the number of descendants:
403
+ ** IMPORTANT!** Any descendant that node has will also be deleted!
404
+
405
+ ** IMPORTANT!** Nodes are required to be deleted as models, ** don't** try do delete them using a query like so:
386
406
387
407
``` php
388
- $node->getDescendantCount ();
408
+ Category::where('id', '=', $id)->delete ();
389
409
```
390
410
411
+ This will brake the tree!
412
+
413
+ ` SoftDeletes ` trait is supported, also on model level.
414
+
415
+ ### Helper methods
416
+
391
417
To check if node is a descendant of other node:
392
418
393
419
``` php
394
420
$bool = $node->isDescendantOf($parent);
395
421
```
396
422
397
- To check whether the node is root:
423
+ To check whether the node is a root:
398
424
399
425
``` php
400
426
$bool = $node->isRoot();
@@ -431,8 +457,8 @@ It will return an array with following keys:
431
457
432
458
#### Fixing tree
433
459
434
- Since v3.1 tree can now be fixed. Using inheritance info from ` parent_id ` column, proper ` _lft ` and ` _rgt ` values
435
- are set for every node.
460
+ Since v3.1 tree can now be fixed. Using inheritance info from ` parent_id ` column,
461
+ proper ` _lft ` and ` _rgt ` values are set for every node.
436
462
437
463
``` php
438
464
Node::fixTree();
@@ -453,11 +479,36 @@ protected function getScopeAttributes()
453
479
}
454
480
```
455
481
456
- But now in order to execute some custom query for retrieving nodes , you need to
457
- provide attributes that are used for scoping:
482
+ But now in order to execute some custom query, you need to provide attributes
483
+ that are used for scoping:
458
484
459
485
``` php
460
- MenuItem::scoped([ 'menu_id' => 5 ])->withDepth()->get();
486
+ MenuItem::scoped([ 'menu_id' => 5 ])->withDepth()->get(); // OK
487
+ MenuItem::descendantsOf($id)->get(); // WRONG: returns nodes from other scope
488
+ MenuItem::scoped([ 'menu_id' => 5 ])->fixTree();
489
+ ```
490
+
491
+ When requesting nodes using model instance, scopes applied automatically based
492
+ on data of that model. See examples:
493
+
494
+ ``` php
495
+ $node = MenuItem::findOrFail($id);
496
+
497
+ $node->siblings()->withDepth()->get(); // OK
498
+ ```
499
+
500
+ To get scoped query builder using instance:
501
+
502
+ ``` php
503
+ $node->newScopedQuery();
504
+ ```
505
+
506
+ Note, that scoping is not required when retrieving model by primary key
507
+ (since the key is unique):
508
+
509
+ ``` php
510
+ $node = MenuItem::findOrFail($id); // OK
511
+ $node = MenuItem::scoped([ 'menu_id' => 5 ])->findOrFail(); // OK, but redundant
461
512
```
462
513
463
514
Requirements
@@ -466,9 +517,6 @@ Requirements
466
517
- PHP >= 5.4
467
518
- Laravel >= 4.1
468
519
469
- Models are extended from new base class rather than ` Eloquent ` , so it's not possible
470
- to use another framework that overrides ` Eloquent ` .
471
-
472
520
It is highly suggested to use database that supports transactions (like MySql's InnoDb)
473
521
to secure a tree from possible corruption.
474
522
0 commit comments