This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace Arcanedev\LaravelNestedSet; |
||
2 | |||
3 | use Arcanedev\LaravelNestedSet\Contracts\Nodeable; |
||
4 | use Arcanedev\LaravelNestedSet\Eloquent\AncestorsRelation; |
||
5 | use Arcanedev\LaravelNestedSet\Eloquent\DescendantsRelation; |
||
6 | use Arcanedev\LaravelNestedSet\Traits\EloquentTrait; |
||
7 | use Arcanedev\LaravelNestedSet\Traits\SoftDeleteTrait; |
||
8 | use Arcanedev\LaravelNestedSet\Utilities\NestedSet; |
||
9 | use Illuminate\Database\Eloquent\Collection as EloquentCollection; |
||
10 | use LogicException; |
||
11 | |||
12 | /** |
||
13 | * Class NodeTrait |
||
14 | * |
||
15 | * @package Arcanedev\LaravelNestedSet |
||
16 | * @author ARCANEDEV <[email protected]> |
||
17 | * |
||
18 | * @property int id |
||
19 | * @property int _lft |
||
20 | * @property int _rgt |
||
21 | * @property int parent_id |
||
22 | * @property array attributes |
||
23 | * @property array original |
||
24 | * @property bool exists |
||
25 | * @property \Arcanedev\LaravelNestedSet\Contracts\Nodeable parent |
||
26 | * @property \Illuminate\Database\Eloquent\Collection children |
||
27 | * |
||
28 | * @method static bool isBroken() |
||
29 | * @method static array getNodeData($id, $required = false) |
||
30 | * @method static array getPlainNodeData($id, $required = false) |
||
31 | * @method static int rebuildTree(array $data, bool $delete = false) |
||
32 | * @method static int fixTree() |
||
33 | * @method static \Arcanedev\LaravelNestedSet\Contracts\Nodeable root(array $columns = ['*']) |
||
34 | * |
||
35 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder ancestorsOf(mixed $id, array $columns = ['*']) |
||
36 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder withDepth(string $as = 'depth') |
||
37 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder withoutRoot() |
||
38 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder whereDescendantOf(mixed $id, string $boolean = 'and', bool $not = false) |
||
39 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder whereAncestorOf(mixed $id) |
||
40 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder whereIsRoot() |
||
41 | * @method static \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder descendantsAndSelf(mixed $id, array $columns = ['*']) |
||
42 | * @method static array countErrors() |
||
43 | * |
||
44 | * @method \Illuminate\Database\Eloquent\Relations\BelongsTo belongsTo(string $related, string $foreignKey = null, string $otherKey = null, string $relation = null) |
||
45 | * @method \Illuminate\Database\Eloquent\Relations\HasMany hasMany(string $related, string $foreignKey = null, string $localKey = null) |
||
46 | * @method static \Illuminate\Database\Eloquent\Builder where(string $column, string $operator = null, mixed $value = null, string $boolean = 'and') |
||
47 | */ |
||
48 | trait NodeTrait |
||
49 | { |
||
50 | /* ------------------------------------------------------------------------------------------------ |
||
51 | | Traits |
||
52 | | ------------------------------------------------------------------------------------------------ |
||
53 | */ |
||
54 | use EloquentTrait, SoftDeleteTrait; |
||
55 | |||
56 | /* ------------------------------------------------------------------------------------------------ |
||
57 | | Properties |
||
58 | | ------------------------------------------------------------------------------------------------ |
||
59 | */ |
||
60 | /** |
||
61 | * Pending operation. |
||
62 | * |
||
63 | * @var array|null |
||
64 | */ |
||
65 | protected $pending; |
||
66 | |||
67 | /** |
||
68 | * Whether the node has moved since last save. |
||
69 | * |
||
70 | * @var bool |
||
71 | */ |
||
72 | protected $moved = false; |
||
73 | |||
74 | /** |
||
75 | * Keep track of the number of performed operations. |
||
76 | * |
||
77 | * @var int |
||
78 | */ |
||
79 | public static $actionsPerformed = 0; |
||
80 | |||
81 | /* ------------------------------------------------------------------------------------------------ |
||
82 | | Boot Function |
||
83 | | ------------------------------------------------------------------------------------------------ |
||
84 | */ |
||
85 | /** |
||
86 | * Sign on model events. |
||
87 | */ |
||
88 | 216 | public static function bootNodeTrait() |
|
89 | { |
||
90 | static::saving(function ($model) { |
||
91 | 75 | return $model->callPendingAction(); |
|
92 | 216 | }); |
|
93 | |||
94 | static::deleting(function ($model) { |
||
95 | // We will need fresh data to delete node safely |
||
96 | 15 | $model->refreshNode(); |
|
97 | 216 | }); |
|
98 | |||
99 | static::deleted(function ($model) { |
||
100 | 15 | $model->deleteDescendants(); |
|
101 | 216 | }); |
|
102 | |||
103 | 216 | if (static::usesSoftDelete()) { |
|
104 | static::restoring(function ($model) { |
||
105 | 3 | static::$deletedAt = $model->{$model->getDeletedAtColumn()}; |
|
106 | 183 | }); |
|
107 | 183 | static::restored(function ($model) { |
|
108 | 3 | $model->restoreDescendants(static::$deletedAt); |
|
109 | 183 | }); |
|
110 | 61 | } |
|
111 | 216 | } |
|
112 | |||
113 | /* ------------------------------------------------------------------------------------------------ |
||
114 | | Relationships |
||
115 | | ------------------------------------------------------------------------------------------------ |
||
116 | */ |
||
117 | /** |
||
118 | * Relation to the parent. |
||
119 | * |
||
120 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
||
121 | */ |
||
122 | 12 | public function parent() |
|
123 | { |
||
124 | 12 | return $this->belongsTo(get_class($this), $this->getParentIdName()) |
|
125 | 12 | ->setModel($this); |
|
126 | } |
||
127 | |||
128 | /** |
||
129 | * Relation to children. |
||
130 | * |
||
131 | * @return \Illuminate\Database\Eloquent\Relations\HasMany |
||
132 | */ |
||
133 | 12 | public function children() |
|
134 | { |
||
135 | 12 | return $this->hasMany(get_class($this), $this->getParentIdName()) |
|
136 | 12 | ->setModel($this); |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * Get query for descendants of the node. |
||
141 | * |
||
142 | * @return \Arcanedev\LaravelNestedSet\Eloquent\DescendantsRelation |
||
143 | */ |
||
144 | 36 | public function descendants() |
|
145 | { |
||
146 | 36 | return new DescendantsRelation($this->newScopedQuery(), $this); |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * Get query for siblings of the node. |
||
151 | * |
||
152 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
153 | */ |
||
154 | 6 | public function siblings() |
|
155 | { |
||
156 | 6 | return $this->newScopedQuery() |
|
157 | 6 | ->where($this->getKeyName(), '<>', $this->getKey()) |
|
158 | 6 | ->where($this->getParentIdName(), '=', $this->getParentId()); |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | * Get query for the node siblings and the node itself. |
||
163 | * |
||
164 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
165 | */ |
||
166 | public function siblingsAndSelf() |
||
167 | { |
||
168 | return $this->newScopedQuery() |
||
169 | ->where($this->getParentIdName(), '=', $this->getParentId()); |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Get the node siblings and the node itself. |
||
174 | * |
||
175 | * @param array $columns |
||
176 | * |
||
177 | * @return \Illuminate\Database\Eloquent\Collection |
||
178 | */ |
||
179 | public function getSiblingsAndSelf(array $columns = ['*']) |
||
180 | { |
||
181 | return $this->siblingsAndSelf()->get($columns); |
||
182 | } |
||
183 | |||
184 | /* ------------------------------------------------------------------------------------------------ |
||
185 | | Getters & Setters |
||
186 | | ------------------------------------------------------------------------------------------------ |
||
187 | */ |
||
188 | /** |
||
189 | * Get the lft key name. |
||
190 | * |
||
191 | * @return string |
||
192 | */ |
||
193 | 198 | public function getLftName() |
|
194 | { |
||
195 | 198 | return NestedSet::LFT; |
|
196 | } |
||
197 | |||
198 | /** |
||
199 | * Get the rgt key name. |
||
200 | * |
||
201 | * @return string |
||
202 | */ |
||
203 | 171 | public function getRgtName() |
|
204 | { |
||
205 | 171 | return NestedSet::RGT; |
|
206 | } |
||
207 | |||
208 | /** |
||
209 | * Get the parent id key name. |
||
210 | * |
||
211 | * @return string |
||
212 | */ |
||
213 | 129 | public function getParentIdName() |
|
214 | { |
||
215 | 129 | return NestedSet::PARENT_ID; |
|
216 | } |
||
217 | |||
218 | /** |
||
219 | * Get the value of the model's lft key. |
||
220 | * |
||
221 | * @return int |
||
222 | */ |
||
223 | 144 | public function getLft() |
|
224 | { |
||
225 | 144 | return $this->getAttributeValue($this->getLftName()); |
|
226 | 1 | } |
|
227 | |||
228 | /** |
||
229 | * Set the value of the model's lft key. |
||
230 | * |
||
231 | * @param int $value |
||
232 | * |
||
233 | * @return self |
||
234 | */ |
||
235 | 45 | public function setLft($value) |
|
236 | { |
||
237 | 45 | $this->attributes[$this->getLftName()] = $value; |
|
238 | |||
239 | 45 | return $this; |
|
240 | } |
||
241 | |||
242 | /** |
||
243 | * Get the value of the model's rgt key. |
||
244 | * |
||
245 | * @return int |
||
246 | */ |
||
247 | 96 | public function getRgt() |
|
248 | { |
||
249 | 96 | return $this->getAttributeValue($this->getRgtName()); |
|
250 | } |
||
251 | |||
252 | /** |
||
253 | * Set the value of the model's rgt key. |
||
254 | * |
||
255 | * @param int $value |
||
256 | * |
||
257 | * @return self |
||
258 | */ |
||
259 | 45 | public function setRgt($value) |
|
260 | { |
||
261 | 45 | $this->attributes[$this->getRgtName()] = $value; |
|
262 | |||
263 | 45 | return $this; |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * Get the value of the model's parent id key. |
||
268 | * |
||
269 | * @return int |
||
270 | */ |
||
271 | 78 | public function getParentId() |
|
272 | { |
||
273 | 78 | return $this->getAttributeValue($this->getParentIdName()); |
|
274 | } |
||
275 | |||
276 | /** |
||
277 | * Set the value of the model's parent id key. |
||
278 | * |
||
279 | * @param int $value |
||
280 | * |
||
281 | * @return self |
||
282 | */ |
||
283 | 51 | public function setParentId($value) |
|
284 | { |
||
285 | 51 | $this->attributes[$this->getParentIdName()] = $value; |
|
286 | |||
287 | 51 | return $this; |
|
288 | } |
||
289 | |||
290 | /** |
||
291 | * Apply parent model. |
||
292 | * |
||
293 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable|null $value |
||
294 | * |
||
295 | * @return self |
||
296 | */ |
||
297 | 42 | protected function setParent($value) |
|
298 | { |
||
299 | 42 | $this->setParentId($value ? $value->getKey() : null) |
|
300 | 42 | ->setRelation('parent', $value); |
|
301 | |||
302 | 42 | return $this; |
|
303 | } |
||
304 | |||
305 | /** |
||
306 | * Set the value of model's parent id key. |
||
307 | * |
||
308 | * Behind the scenes node is appended to found parent node. |
||
309 | * |
||
310 | * @param int $value |
||
311 | * |
||
312 | * @throws \Exception If parent node doesn't exists |
||
313 | */ |
||
314 | 9 | public function setParentIdAttribute($value) |
|
315 | { |
||
316 | 9 | if ($this->getParentId() == $value) return; |
|
317 | |||
318 | 9 | if ($value) { |
|
319 | /** @var \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node */ |
||
320 | 9 | $node = $this->newScopedQuery()->findOrFail($value); |
|
321 | |||
322 | 9 | $this->appendToNode($node); |
|
323 | 3 | } else { |
|
324 | 3 | $this->makeRoot(); |
|
325 | } |
||
326 | 9 | } |
|
327 | |||
328 | /** |
||
329 | * Get the boundaries. |
||
330 | * |
||
331 | * @return array |
||
332 | */ |
||
333 | 36 | public function getBounds() |
|
334 | { |
||
335 | 36 | return [$this->getLft(), $this->getRgt()]; |
|
336 | } |
||
337 | |||
338 | /** |
||
339 | * Get the scope attributes. |
||
340 | * |
||
341 | * @return array |
||
342 | */ |
||
343 | 123 | protected function getScopeAttributes() |
|
344 | { |
||
345 | 123 | return null; |
|
346 | } |
||
347 | |||
348 | /** |
||
349 | * Set the lft and rgt boundaries to null. |
||
350 | * |
||
351 | * @return self |
||
352 | */ |
||
353 | 51 | protected function dirtyBounds() |
|
354 | { |
||
355 | 51 | $this->original[$this->getLftName()] = null; |
|
356 | 51 | $this->original[$this->getRgtName()] = null; |
|
357 | |||
358 | 51 | return $this; |
|
359 | } |
||
360 | |||
361 | /** |
||
362 | * Returns node that is next to current node without constraining to siblings. |
||
363 | * This can be either a next sibling or a next sibling of the parent node. |
||
364 | * |
||
365 | * @param array $columns |
||
366 | * |
||
367 | * @return self |
||
368 | */ |
||
369 | public function getNextNode(array $columns = ['*']) |
||
370 | { |
||
371 | return $this->nextNodes()->defaultOrder()->first($columns); |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * Returns node that is before current node without constraining to siblings. |
||
376 | * This can be either a prev sibling or parent node. |
||
377 | * |
||
378 | * @param array $columns |
||
379 | * |
||
380 | * @return self |
||
381 | */ |
||
382 | 3 | public function getPrevNode(array $columns = ['*']) |
|
383 | { |
||
384 | 3 | return $this->prevNodes()->defaultOrder('desc')->first($columns); |
|
385 | } |
||
386 | |||
387 | /** |
||
388 | * Get the ancestors nodes. |
||
389 | * |
||
390 | * @param array $columns |
||
391 | * |
||
392 | * @return \Arcanedev\LaravelNestedSet\Eloquent\Collection |
||
393 | */ |
||
394 | 6 | public function getAncestors(array $columns = ['*']) |
|
395 | { |
||
396 | 6 | return $this->newScopedQuery() |
|
397 | 6 | ->defaultOrder() |
|
398 | 6 | ->ancestorsOf($this, $columns); |
|
399 | } |
||
400 | |||
401 | /** |
||
402 | * Get the descendants nodes. |
||
403 | * |
||
404 | * @param array $columns |
||
405 | * |
||
406 | * @return \Arcanedev\LaravelNestedSet\Eloquent\Collection |
||
407 | */ |
||
408 | 9 | public function getDescendants(array $columns = ['*']) |
|
409 | { |
||
410 | 9 | return $this->descendants()->get($columns); |
|
411 | } |
||
412 | |||
413 | /** |
||
414 | * Get the siblings nodes. |
||
415 | * |
||
416 | * @param array $columns |
||
417 | * |
||
418 | * @return \Arcanedev\LaravelNestedSet\Eloquent\Collection |
||
419 | */ |
||
420 | 6 | public function getSiblings(array $columns = ['*']) |
|
421 | { |
||
422 | 6 | return $this->siblings()->get($columns); |
|
423 | } |
||
424 | |||
425 | /** |
||
426 | * Get the next siblings nodes. |
||
427 | * |
||
428 | * @param array $columns |
||
429 | * |
||
430 | * @return \Arcanedev\LaravelNestedSet\Eloquent\Collection |
||
431 | */ |
||
432 | 6 | public function getNextSiblings(array $columns = ['*']) |
|
433 | { |
||
434 | 6 | return $this->nextSiblings()->get($columns); |
|
435 | } |
||
436 | |||
437 | /** |
||
438 | * Get the previous siblings nodes. |
||
439 | * |
||
440 | * @param array $columns |
||
441 | * |
||
442 | * @return \Arcanedev\LaravelNestedSet\Eloquent\Collection |
||
443 | */ |
||
444 | 6 | public function getPrevSiblings(array $columns = ['*']) |
|
445 | { |
||
446 | 6 | return $this->prevSiblings()->get($columns); |
|
447 | } |
||
448 | |||
449 | /** |
||
450 | * Get the next sibling node. |
||
451 | * |
||
452 | * @param array $columns |
||
453 | * |
||
454 | * @return self |
||
455 | */ |
||
456 | 3 | public function getNextSibling(array $columns = ['*']) |
|
457 | { |
||
458 | 3 | return $this->nextSiblings()->defaultOrder()->first($columns); |
|
459 | } |
||
460 | |||
461 | /** |
||
462 | * Get the previous sibling node. |
||
463 | * |
||
464 | * @param array $columns |
||
465 | * |
||
466 | * @return self |
||
467 | */ |
||
468 | 3 | public function getPrevSibling(array $columns = ['*']) |
|
469 | { |
||
470 | 3 | return $this->prevSiblings()->defaultOrder('desc')->first($columns); |
|
471 | } |
||
472 | |||
473 | /** |
||
474 | * Get node height (rgt - lft + 1). |
||
475 | * |
||
476 | * @return int |
||
477 | */ |
||
478 | 27 | public function getNodeHeight() |
|
479 | { |
||
480 | 27 | if ( ! $this->exists) return 2; |
|
481 | |||
482 | 3 | return $this->getRgt() - $this->getLft() + 1; |
|
483 | } |
||
484 | |||
485 | /** |
||
486 | * Get number of descendant nodes. |
||
487 | * |
||
488 | * @return int |
||
489 | */ |
||
490 | 3 | public function getDescendantCount() |
|
491 | { |
||
492 | 3 | return (int) ceil($this->getNodeHeight() / 2) - 1; |
|
493 | } |
||
494 | |||
495 | /** |
||
496 | * Set raw node. |
||
497 | * |
||
498 | * @param int $lft |
||
499 | * @param int $rgt |
||
500 | * @param int $parentId |
||
501 | * |
||
502 | * @return self |
||
503 | */ |
||
504 | 9 | public function rawNode($lft, $rgt, $parentId) |
|
505 | { |
||
506 | 9 | $this->setLft($lft)->setRgt($rgt)->setParentId($parentId); |
|
507 | |||
508 | 9 | return $this->setNodeAction('raw'); |
|
509 | } |
||
510 | |||
511 | /** |
||
512 | * Set an action. |
||
513 | * |
||
514 | * @param string $action |
||
515 | * |
||
516 | * @return self |
||
517 | */ |
||
518 | 90 | protected function setNodeAction($action) |
|
519 | { |
||
520 | 90 | $this->pending = func_get_args(); |
|
521 | 90 | unset($action); |
|
522 | |||
523 | 90 | return $this; |
|
524 | } |
||
525 | |||
526 | /* ------------------------------------------------------------------------------------------------ |
||
527 | | Other Functions |
||
528 | | ------------------------------------------------------------------------------------------------ |
||
529 | */ |
||
530 | /** |
||
531 | * Get the lower bound. |
||
532 | * |
||
533 | * @return int |
||
534 | */ |
||
535 | 24 | protected function getLowerBound() |
|
536 | { |
||
537 | 24 | return (int) $this->newNestedSetQuery()->max($this->getRgtName()); |
|
538 | } |
||
539 | |||
540 | /** |
||
541 | * Call pending action. |
||
542 | */ |
||
543 | 75 | protected function callPendingAction() |
|
544 | { |
||
545 | 75 | $this->moved = false; |
|
546 | |||
547 | 75 | if ( ! $this->pending && ! $this->exists) { |
|
548 | 15 | $this->makeRoot(); |
|
549 | 5 | } |
|
550 | |||
551 | 75 | if ( ! $this->pending) return; |
|
552 | |||
553 | 72 | $method = 'action'.ucfirst(array_shift($this->pending)); |
|
554 | 72 | $parameters = $this->pending; |
|
555 | |||
556 | 72 | $this->pending = null; |
|
557 | 72 | $this->moved = call_user_func_array([$this, $method], $parameters); |
|
558 | 72 | } |
|
559 | |||
560 | /** |
||
561 | * @return bool |
||
562 | */ |
||
563 | 9 | protected function actionRaw() |
|
564 | { |
||
565 | 9 | return true; |
|
566 | } |
||
567 | |||
568 | /** |
||
569 | * Make a root node. |
||
570 | */ |
||
571 | 24 | protected function actionRoot() |
|
572 | { |
||
573 | // Simplest case that do not affect other nodes. |
||
574 | 24 | if ( ! $this->exists) { |
|
575 | 15 | $cut = $this->getLowerBound() + 1; |
|
576 | |||
577 | 15 | $this->setLft($cut); |
|
578 | 15 | $this->setRgt($cut + 1); |
|
579 | |||
580 | 15 | return true; |
|
581 | } |
||
582 | |||
583 | 9 | if ($this->isRoot()) return false; |
|
584 | |||
585 | |||
586 | // Reset parent object |
||
587 | 9 | $this->setParent(null); |
|
588 | |||
589 | 9 | return $this->insertAt($this->getLowerBound() + 1); |
|
590 | } |
||
591 | |||
592 | /** |
||
593 | * Append or prepend a node to the parent. |
||
594 | * |
||
595 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $parent |
||
596 | * @param bool $prepend |
||
597 | * |
||
598 | * @return bool |
||
599 | */ |
||
600 | 24 | protected function actionAppendOrPrepend(Nodeable $parent, $prepend = false) |
|
601 | { |
||
602 | 24 | $parent->refreshNode(); |
|
603 | |||
604 | 24 | $cut = $prepend ? $parent->getLft() + 1 : $parent->getRgt(); |
|
605 | |||
606 | 24 | if ( ! $this->insertAt($cut)) { |
|
607 | return false; |
||
608 | } |
||
609 | |||
610 | 24 | $parent->refreshNode(); |
|
611 | |||
612 | 24 | return true; |
|
613 | } |
||
614 | |||
615 | /** |
||
616 | * Insert node before or after another node. |
||
617 | * |
||
618 | * @param self $node |
||
619 | * @param bool $after |
||
620 | * |
||
621 | * @return bool |
||
622 | */ |
||
623 | 21 | protected function actionBeforeOrAfter(self $node, $after = false) |
|
624 | { |
||
625 | 21 | $node->refreshNode(); |
|
626 | |||
627 | 21 | return $this->insertAt($after ? $node->getRgt() + 1 : $node->getLft()); |
|
628 | } |
||
629 | |||
630 | /** |
||
631 | * Refresh node's crucial attributes. |
||
632 | */ |
||
633 | 66 | public function refreshNode() |
|
634 | { |
||
635 | 66 | if ( ! $this->exists || static::$actionsPerformed === 0) return; |
|
636 | |||
637 | 48 | $attributes = $this->newNestedSetQuery()->getNodeData($this->getKey()); |
|
638 | |||
639 | 48 | $this->attributes = array_merge($this->attributes, $attributes); |
|
640 | 48 | } |
|
641 | |||
642 | /** |
||
643 | * Get query for siblings after the node. |
||
644 | * |
||
645 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
646 | */ |
||
647 | 18 | public function nextSiblings() |
|
648 | { |
||
649 | 18 | return $this->nextNodes() |
|
650 | 18 | ->where($this->getParentIdName(), '=', $this->getParentId()); |
|
651 | } |
||
652 | |||
653 | /** |
||
654 | * Get query for siblings before the node. |
||
655 | * |
||
656 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
657 | */ |
||
658 | 12 | public function prevSiblings() |
|
659 | { |
||
660 | 12 | return $this->prevNodes() |
|
661 | 12 | ->where($this->getParentIdName(), '=', $this->getParentId()); |
|
662 | } |
||
663 | |||
664 | /** |
||
665 | * Get query for nodes after current node. |
||
666 | * |
||
667 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
668 | */ |
||
669 | 21 | public function nextNodes() |
|
670 | { |
||
671 | 21 | return $this->newScopedQuery() |
|
672 | 21 | ->where($this->getLftName(), '>', $this->getLft()); |
|
673 | } |
||
674 | |||
675 | /** |
||
676 | * Get query for nodes before current node in reversed order. |
||
677 | * |
||
678 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
679 | */ |
||
680 | 15 | public function prevNodes() |
|
681 | { |
||
682 | 15 | return $this->newScopedQuery() |
|
683 | 15 | ->where($this->getLftName(), '<', $this->getLft()); |
|
684 | } |
||
685 | |||
686 | /** |
||
687 | * Get query for ancestors to the node not including the node itself. |
||
688 | * |
||
689 | * @return \Arcanedev\LaravelNestedSet\Eloquent\AncestorsRelation |
||
690 | */ |
||
691 | 3 | public function ancestors() |
|
692 | { |
||
693 | 3 | return new AncestorsRelation($this->newScopedQuery(), $this); |
|
694 | } |
||
695 | |||
696 | /** |
||
697 | * Make this node a root node. |
||
698 | * |
||
699 | * @return self |
||
700 | */ |
||
701 | 36 | public function makeRoot() |
|
702 | { |
||
703 | 36 | return $this->setNodeAction('root'); |
|
704 | } |
||
705 | |||
706 | /** |
||
707 | * Save node as root. |
||
708 | * |
||
709 | * @return bool |
||
710 | */ |
||
711 | 6 | public function saveAsRoot() |
|
712 | { |
||
713 | 6 | return ($this->exists && $this->isRoot()) |
|
714 | 2 | ? $this->save() |
|
715 | 6 | : $this->makeRoot()->save(); |
|
716 | } |
||
717 | |||
718 | /** |
||
719 | * Append and save a node. |
||
720 | * |
||
721 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
722 | * |
||
723 | * @return bool |
||
724 | */ |
||
725 | 12 | public function appendNode(Nodeable $node) |
|
726 | { |
||
727 | 12 | return $node->appendToNode($this)->save(); |
|
728 | } |
||
729 | |||
730 | /** |
||
731 | * Prepend and save a node. |
||
732 | * |
||
733 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
734 | * |
||
735 | * @return bool |
||
736 | */ |
||
737 | 3 | public function prependNode(Nodeable $node) |
|
738 | { |
||
739 | 3 | return $node->prependToNode($this)->save(); |
|
740 | } |
||
741 | |||
742 | /** |
||
743 | * Append a node to the new parent. |
||
744 | * |
||
745 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $parent |
||
746 | * |
||
747 | * @return self |
||
748 | */ |
||
749 | 33 | public function appendToNode(Nodeable $parent) |
|
750 | { |
||
751 | 33 | return $this->appendOrPrependTo($parent); |
|
752 | } |
||
753 | |||
754 | /** |
||
755 | * Prepend a node to the new parent. |
||
756 | * |
||
757 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $parent |
||
758 | * |
||
759 | * @return \Arcanedev\LaravelNestedSet\Contracts\Nodeable |
||
760 | */ |
||
761 | 6 | public function prependToNode(Nodeable $parent) |
|
762 | { |
||
763 | 6 | return $this->appendOrPrependTo($parent, true); |
|
764 | } |
||
765 | |||
766 | /** |
||
767 | * Append or prepend a node to parent. |
||
768 | * |
||
769 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $parent |
||
770 | * @param bool $prepend |
||
771 | * |
||
772 | * @return \Arcanedev\LaravelNestedSet\Contracts\Nodeable |
||
773 | */ |
||
774 | 39 | public function appendOrPrependTo(Nodeable $parent, $prepend = false) |
|
775 | { |
||
776 | 39 | $this->assertNodeExists($parent) |
|
777 | 36 | ->assertNotDescendant($parent); |
|
778 | |||
779 | 30 | $this->setParent($parent)->dirtyBounds(); |
|
780 | |||
781 | 30 | return $this->setNodeAction('appendOrPrepend', $parent, $prepend); |
|
782 | } |
||
783 | |||
784 | /** |
||
785 | * Insert self after a node. |
||
786 | * |
||
787 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
788 | * |
||
789 | * @return \Arcanedev\LaravelNestedSet\Contracts\Nodeable |
||
790 | */ |
||
791 | 21 | public function afterNode(Nodeable $node) |
|
792 | { |
||
793 | 21 | return $this->beforeOrAfterNode($node, true); |
|
794 | } |
||
795 | |||
796 | /** |
||
797 | * Insert self before node. |
||
798 | * |
||
799 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
800 | * |
||
801 | * @return \Arcanedev\LaravelNestedSet\Contracts\Nodeable |
||
802 | */ |
||
803 | 6 | public function beforeNode(Nodeable $node) |
|
804 | { |
||
805 | 6 | return $this->beforeOrAfterNode($node); |
|
806 | } |
||
807 | |||
808 | /** |
||
809 | * Set before or after a node. |
||
810 | * |
||
811 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
812 | * @param bool $after |
||
813 | * |
||
814 | * @return \Arcanedev\LaravelNestedSet\Contracts\Nodeable |
||
815 | */ |
||
816 | 27 | public function beforeOrAfterNode(Nodeable $node, $after = false) |
|
817 | { |
||
818 | 27 | $this->assertNodeExists($node)->assertNotDescendant($node); |
|
819 | |||
820 | 24 | if ( ! $this->isSiblingOf($node)) { |
|
821 | 9 | $this->setParent($node->getRelationValue('parent')); |
|
822 | 3 | } |
|
823 | |||
824 | 24 | $this->dirtyBounds(); |
|
825 | |||
826 | 24 | return $this->setNodeAction('beforeOrAfter', $node, $after); |
|
827 | } |
||
828 | |||
829 | /** |
||
830 | * Insert after a node and save. |
||
831 | * |
||
832 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
833 | * |
||
834 | * @return bool |
||
835 | */ |
||
836 | 12 | public function insertAfterNode(Nodeable $node) |
|
837 | { |
||
838 | 12 | return $this->afterNode($node)->save(); |
|
839 | } |
||
840 | |||
841 | /** |
||
842 | * Insert self before a node and save. |
||
843 | * |
||
844 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
845 | * |
||
846 | * @return bool |
||
847 | */ |
||
848 | 3 | public function insertBeforeNode(Nodeable $node) |
|
849 | { |
||
850 | 3 | if ( ! $this->beforeNode($node)->save()) return false; |
|
851 | |||
852 | // We'll update the target node since it will be moved |
||
853 | 3 | $node->refreshNode(); |
|
854 | |||
855 | 3 | return true; |
|
856 | } |
||
857 | |||
858 | /** |
||
859 | * Move node up given amount of positions. |
||
860 | * |
||
861 | * @param int $amount |
||
862 | * |
||
863 | * @return bool |
||
864 | */ |
||
865 | 3 | public function up($amount = 1) |
|
866 | { |
||
867 | 3 | $sibling = $this->prevSiblings() |
|
868 | 3 | ->defaultOrder('desc') |
|
869 | 3 | ->skip($amount - 1) |
|
870 | 3 | ->first(); |
|
871 | |||
872 | 3 | if ( ! $sibling) return false; |
|
873 | |||
874 | 3 | return $this->insertBeforeNode($sibling); |
|
875 | } |
||
876 | |||
877 | /** |
||
878 | * Move node down given amount of positions. |
||
879 | * |
||
880 | * @param int $amount |
||
881 | * |
||
882 | * @return bool |
||
883 | */ |
||
884 | 12 | public function down($amount = 1) |
|
885 | { |
||
886 | 12 | $sibling = $this->nextSiblings() |
|
887 | 12 | ->defaultOrder() |
|
888 | 12 | ->skip($amount - 1) |
|
889 | 12 | ->first(); |
|
890 | |||
891 | 12 | if ( ! $sibling) return false; |
|
892 | |||
893 | 12 | return $this->insertAfterNode($sibling); |
|
894 | } |
||
895 | |||
896 | /** |
||
897 | * Insert node at specific position. |
||
898 | * |
||
899 | * @param int $position |
||
900 | * |
||
901 | * @return bool |
||
902 | */ |
||
903 | 51 | protected function insertAt($position) |
|
904 | { |
||
905 | 51 | ++static::$actionsPerformed; |
|
906 | |||
907 | 51 | $result = $this->exists |
|
908 | 37 | ? $this->moveNode($position) |
|
909 | 51 | : $this->insertNode($position); |
|
910 | |||
911 | 51 | return $result; |
|
912 | } |
||
913 | |||
914 | /** |
||
915 | * Move a node to the new position. |
||
916 | * |
||
917 | * @param int $position |
||
918 | * |
||
919 | * @return int |
||
920 | */ |
||
921 | 30 | protected function moveNode($position) |
|
922 | { |
||
923 | 30 | $updated = $this->newNestedSetQuery() |
|
924 | 30 | ->moveNode($this->getKey(), $position) > 0; |
|
925 | |||
926 | 30 | if ($updated) $this->refreshNode(); |
|
927 | |||
928 | 30 | return $updated; |
|
929 | } |
||
930 | |||
931 | /** |
||
932 | * Insert new node at specified position. |
||
933 | * |
||
934 | * @param int $position |
||
935 | * |
||
936 | * @return bool |
||
937 | */ |
||
938 | 24 | protected function insertNode($position) |
|
939 | { |
||
940 | 24 | $this->newNestedSetQuery()->makeGap($position, 2); |
|
941 | |||
942 | 24 | $height = $this->getNodeHeight(); |
|
943 | |||
944 | 24 | $this->setLft($position); |
|
945 | 24 | $this->setRgt($position + $height - 1); |
|
946 | |||
947 | 24 | return true; |
|
948 | } |
||
949 | |||
950 | /** |
||
951 | * Update the tree when the node is removed physically. |
||
952 | */ |
||
953 | 15 | protected function deleteDescendants() |
|
954 | { |
||
955 | 15 | $lft = $this->getLft(); |
|
956 | 15 | $rgt = $this->getRgt(); |
|
957 | |||
958 | 15 | $method = ($this->usesSoftDelete() && $this->forceDeleting) |
|
959 | 11 | ? 'forceDelete' |
|
960 | 15 | : 'delete'; |
|
961 | |||
962 | 15 | $this->descendants()->{$method}(); |
|
963 | |||
964 | 15 | if ($this->hardDeleting()) { |
|
965 | 12 | $height = $rgt - $lft + 1; |
|
966 | |||
967 | 12 | $this->newNestedSetQuery()->makeGap($rgt + 1, -$height); |
|
968 | |||
969 | // In case if user wants to re-create the node |
||
970 | 12 | $this->makeRoot(); |
|
971 | |||
972 | 12 | static::$actionsPerformed++; |
|
973 | 4 | } |
|
974 | 15 | } |
|
975 | |||
976 | /** |
||
977 | * Restore the descendants. |
||
978 | * |
||
979 | * @param \Carbon\Carbon $deletedAt |
||
980 | */ |
||
981 | 3 | protected function restoreDescendants($deletedAt) |
|
982 | { |
||
983 | 3 | $this->descendants() |
|
984 | 3 | ->where($this->getDeletedAtColumn(), '>=', $deletedAt) |
|
985 | 3 | ->applyScopes() |
|
986 | 3 | ->restore(); |
|
987 | 3 | } |
|
988 | |||
989 | /** |
||
990 | * Get a new base query that includes deleted nodes. |
||
991 | * |
||
992 | * @param string|null $table |
||
993 | * |
||
994 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
995 | */ |
||
996 | 90 | public function newNestedSetQuery($table = null) |
|
997 | { |
||
998 | 90 | $builder = $this->usesSoftDelete() |
|
999 | 78 | ? $this->withTrashed() |
|
1000 | 90 | : $this->newQuery(); |
|
1001 | |||
1002 | 90 | return $this->applyNestedSetScope($builder, $table); |
|
1003 | } |
||
1004 | |||
1005 | /** |
||
1006 | * Create a new scoped query. |
||
1007 | * |
||
1008 | * @param string|null $table |
||
1009 | * |
||
1010 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder |
||
1011 | */ |
||
1012 | 105 | public function newScopedQuery($table = null) |
|
1013 | { |
||
1014 | 105 | return $this->applyNestedSetScope($this->newQuery(), $table); |
|
1015 | } |
||
1016 | |||
1017 | /** |
||
1018 | * Apply the nested set scope. |
||
1019 | * |
||
1020 | * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query |
||
1021 | * @param string $table |
||
1022 | * |
||
1023 | * @return \Arcanedev\LaravelNestedSet\Eloquent\QueryBuilder|\Illuminate\Database\Query\Builder |
||
1024 | */ |
||
1025 | 156 | public function applyNestedSetScope($query, $table = null) |
|
1026 | { |
||
1027 | 156 | if ( ! $scoped = $this->getScopeAttributes()) { |
|
1028 | 123 | return $query; |
|
1029 | } |
||
1030 | |||
1031 | 33 | if ($table === null) { |
|
1032 | 33 | $table = $this->getTable(); |
|
1033 | 11 | } |
|
1034 | |||
1035 | 33 | foreach ($scoped as $attribute) { |
|
1036 | 33 | $query->where("$table.$attribute", '=', $this->getAttributeValue($attribute)); |
|
1037 | 11 | } |
|
1038 | |||
1039 | 33 | return $query; |
|
1040 | } |
||
1041 | |||
1042 | /** |
||
1043 | * @param array $attributes |
||
1044 | * |
||
1045 | * @return self |
||
1046 | */ |
||
1047 | 9 | public static function scoped(array $attributes) |
|
1048 | { |
||
1049 | 9 | $instance = new static; |
|
1050 | |||
1051 | 9 | $instance->setRawAttributes($attributes); |
|
1052 | |||
1053 | 9 | return $instance->newScopedQuery(); |
|
1054 | } |
||
1055 | |||
1056 | /** |
||
1057 | * Save a new model and return the instance. |
||
1058 | * |
||
1059 | * Use `children` key on `$attributes` to create child nodes. |
||
1060 | * |
||
1061 | * @param array $attributes |
||
1062 | * @param self $parent |
||
1063 | * |
||
1064 | * @return self |
||
1065 | */ |
||
1066 | 9 | public static function create(array $attributes = [], self $parent = null) |
|
1067 | { |
||
1068 | 9 | $children = array_pull($attributes, 'children'); |
|
1069 | 9 | $instance = new static($attributes); |
|
0 ignored issues
–
show
|
|||
1070 | |||
1071 | 9 | if ($parent) { |
|
1072 | 3 | $instance->appendToNode($parent); |
|
1073 | 1 | } |
|
1074 | |||
1075 | 9 | $instance->save(); |
|
1076 | |||
1077 | // Now create children |
||
1078 | 9 | $relation = new EloquentCollection; |
|
1079 | |||
1080 | 9 | foreach ((array) $children as $child) { |
|
1081 | 3 | $relation->add($child = static::create($child, $instance)); |
|
1082 | |||
1083 | 3 | $child->setRelation('parent', $instance); |
|
1084 | 3 | } |
|
1085 | |||
1086 | 9 | return $instance->setRelation('children', $relation); |
|
1087 | } |
||
1088 | |||
1089 | /* ------------------------------------------------------------------------------------------------ |
||
1090 | | Check Functions |
||
1091 | | ------------------------------------------------------------------------------------------------ |
||
1092 | */ |
||
1093 | /** |
||
1094 | * Get whether node is root. |
||
1095 | * |
||
1096 | * @return bool |
||
1097 | */ |
||
1098 | 12 | public function isRoot() |
|
1099 | { |
||
1100 | 12 | return is_null($this->getParentId()); |
|
1101 | } |
||
1102 | |||
1103 | /** |
||
1104 | * Get whether a node is a descendant of other node. |
||
1105 | * |
||
1106 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
1107 | * |
||
1108 | * @return bool |
||
1109 | */ |
||
1110 | 57 | public function isDescendantOf(Nodeable $node) |
|
1111 | { |
||
1112 | return ( |
||
1113 | 57 | $this->getLft() > $node->getLft() && |
|
1114 | 55 | $this->getLft() < $node->getRgt() |
|
1115 | 19 | ); |
|
1116 | } |
||
1117 | |||
1118 | /** |
||
1119 | * Get whether the node is immediate children of other node. |
||
1120 | * |
||
1121 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
1122 | * |
||
1123 | * @return bool |
||
1124 | */ |
||
1125 | 3 | public function isChildOf(Nodeable $node) |
|
1126 | { |
||
1127 | 3 | return $this->getParentId() == $node->getKey(); |
|
1128 | } |
||
1129 | |||
1130 | /** |
||
1131 | * Get whether the node is a sibling of another node. |
||
1132 | * |
||
1133 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
1134 | * |
||
1135 | * @return bool |
||
1136 | */ |
||
1137 | 24 | public function isSiblingOf(Nodeable $node) |
|
1138 | { |
||
1139 | 24 | return $this->getParentId() == $node->getParentId(); |
|
1140 | } |
||
1141 | |||
1142 | /** |
||
1143 | * Get whether the node is an ancestor of other node, including immediate parent. |
||
1144 | * |
||
1145 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
1146 | * |
||
1147 | * @return bool |
||
1148 | */ |
||
1149 | 3 | public function isAncestorOf(Nodeable $node) |
|
1150 | { |
||
1151 | /** @var \Arcanedev\LaravelNestedSet\Contracts\Nodeable $this */ |
||
1152 | 3 | return $node->isDescendantOf($this); |
|
1153 | } |
||
1154 | |||
1155 | /** |
||
1156 | * Get whether the node has moved since last save. |
||
1157 | * |
||
1158 | * @return bool |
||
1159 | */ |
||
1160 | 18 | public function hasMoved() |
|
1161 | { |
||
1162 | 18 | return $this->moved; |
|
1163 | } |
||
1164 | |||
1165 | /* ------------------------------------------------------------------------------------------------ |
||
1166 | | Assertion Functions |
||
1167 | | ------------------------------------------------------------------------------------------------ |
||
1168 | */ |
||
1169 | /** |
||
1170 | * Assert that the node is not a descendant. |
||
1171 | * |
||
1172 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
1173 | * |
||
1174 | * @return self |
||
1175 | * |
||
1176 | * @throws \LogicException |
||
1177 | */ |
||
1178 | 60 | protected function assertNotDescendant(Nodeable $node) |
|
1179 | { |
||
1180 | /** @var \Arcanedev\LaravelNestedSet\Contracts\Nodeable $this */ |
||
1181 | 60 | if ($node == $this || $node->isDescendantOf($this)) { |
|
1182 | 9 | throw new LogicException('Node must not be a descendant.'); |
|
1183 | } |
||
1184 | |||
1185 | 51 | return $this; |
|
1186 | } |
||
1187 | |||
1188 | /** |
||
1189 | * Assert node exists. |
||
1190 | * |
||
1191 | * @param \Arcanedev\LaravelNestedSet\Contracts\Nodeable $node |
||
1192 | * |
||
1193 | * @return self |
||
1194 | * |
||
1195 | * @throws \LogicException |
||
1196 | */ |
||
1197 | 63 | protected function assertNodeExists(Nodeable $node) |
|
1198 | { |
||
1199 | 63 | if ( ! $node->getLft() || ! $node->getRgt()) { |
|
1200 | 3 | throw new LogicException('Node must exists.'); |
|
1201 | } |
||
1202 | |||
1203 | 60 | return $this; |
|
1204 | } |
||
1205 | } |
||
1206 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.