Passed
Push — master ( 19a44e...ffb10f )
by Jonas
05:12 queued 02:24
created

HasAdjacencyList::rootAncestor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Staudenmeir\LaravelAdjacencyList\Eloquent\Traits;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Str;
8
use Staudenmeir\LaravelAdjacencyList\Eloquent\Collection;
9
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors;
10
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline;
11
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants;
12
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor;
13
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings;
14
15
trait HasAdjacencyList
16
{
17
    use HasOfDescendantsRelationships;
18
    use HasRecursiveRelationshipScopes;
19
20
    /**
21
     * The additional constraint for the recursive query.
22
     *
23
     * @var callable|null
24
     */
25
    public static $recursiveQueryConstraint;
26
27
    /**
28
     * Get the name of the parent key column.
29
     *
30
     * @return string
31
     */
32 870
    public function getParentKeyName()
33
    {
34 870
        return 'parent_id';
35
    }
36
37
    /**
38
     * Get the qualified parent key column.
39
     *
40
     * @return string
41
     */
42 446
    public function getQualifiedParentKeyName()
43
    {
44 446
        return (new static())->getTable().'.'.$this->getParentKeyName();
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

44
        return (new static())->/** @scrutinizer ignore-call */ getTable().'.'.$this->getParentKeyName();
Loading history...
45
    }
46
47
    /**
48
     * Get the name of the local key column.
49
     *
50
     * @return string
51
     */
52 835
    public function getLocalKeyName()
53
    {
54 835
        return $this->getKeyName();
0 ignored issues
show
Bug introduced by
It seems like getKeyName() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

54
        return $this->/** @scrutinizer ignore-call */ getKeyName();
Loading history...
55
    }
56
57
    /**
58
     * Get the qualified local key column.
59
     *
60
     * @return string
61
     */
62 503
    public function getQualifiedLocalKeyName()
63
    {
64 503
        return $this->qualifyColumn($this->getLocalKeyName());
0 ignored issues
show
Bug introduced by
It seems like qualifyColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

64
        return $this->/** @scrutinizer ignore-call */ qualifyColumn($this->getLocalKeyName());
Loading history...
65
    }
66
67
    /**
68
     * Get the name of the depth column.
69
     *
70
     * @return string
71
     */
72 785
    public function getDepthName()
73
    {
74 785
        return 'depth';
75
    }
76
77
    /**
78
     * Get the name of the path column.
79
     *
80
     * @return string
81
     */
82 613
    public function getPathName()
83
    {
84 613
        return 'path';
85
    }
86
87
    /**
88
     * Get the path separator.
89
     *
90
     * @return string
91
     */
92 544
    public function getPathSeparator()
93
    {
94 544
        return '.';
95
    }
96
97
    /**
98
     * Get the additional custom paths.
99
     *
100
     * @return array
101
     */
102 459
    public function getCustomPaths()
103
    {
104 459
        return [];
105
    }
106
107
    /**
108
     * Get the name of the common table expression.
109
     *
110
     * @return string
111
     */
112 785
    public function getExpressionName()
113
    {
114 785
        return 'laravel_cte';
115
    }
116
117
    /**
118
     * Get the model's ancestors.
119
     *
120
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
121
     */
122 113
    public function ancestors()
123
    {
124 113
        return $this->newAncestors(
125 113
            (new static())->newQuery(),
0 ignored issues
show
Bug introduced by
It seems like newQuery() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
            (new static())->/** @scrutinizer ignore-call */ newQuery(),
Loading history...
126 113
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ncyList::newAncestors(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
            /** @scrutinizer ignore-type */ $this,
Loading history...
127 113
            $this->getQualifiedParentKeyName(),
128 113
            $this->getLocalKeyName(),
129 113
            false
130 113
        );
131
    }
132
133
    /**
134
     * Get the model's ancestors and itself.
135
     *
136
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
137
     */
138 55
    public function ancestorsAndSelf()
139
    {
140 55
        return $this->newAncestors(
141 55
            (new static())->newQuery(),
142 55
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ncyList::newAncestors(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

142
            /** @scrutinizer ignore-type */ $this,
Loading history...
143 55
            $this->getQualifiedParentKeyName(),
144 55
            $this->getLocalKeyName(),
145 55
            true
146 55
        );
147
    }
148
149
    /**
150
     * Instantiate a new Ancestors relationship.
151
     *
152
     * @param \Illuminate\Database\Eloquent\Builder $query
153
     * @param \Illuminate\Database\Eloquent\Model $parent
154
     * @param string $foreignKey
155
     * @param string $localKey
156
     * @param bool $andSelf
157
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
158
     */
159 168
    protected function newAncestors(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
160
    {
161 168
        return new Ancestors($query, $parent, $foreignKey, $localKey, $andSelf);
162
    }
163
164
    /**
165
     * Get the model's bloodline.
166
     *
167
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline
168
     */
169 60
    public function bloodline()
170
    {
171 60
        return $this->newBloodline(
172 60
            (new static())->newQuery(),
173 60
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ncyList::newBloodline(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

173
            /** @scrutinizer ignore-type */ $this,
Loading history...
174 60
            $this->getQualifiedParentKeyName(),
175 60
            $this->getLocalKeyName()
176 60
        );
177
    }
178
179
    /**
180
     * Instantiate a new Bloodline relationship.
181
     *
182
     * @param \Illuminate\Database\Eloquent\Builder $query
183
     * @param \Illuminate\Database\Eloquent\Model $parent
184
     * @param string $foreignKey
185
     * @param string $localKey
186
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline
187
     */
188 60
    protected function newBloodline(Builder $query, Model $parent, $foreignKey, $localKey)
189
    {
190 60
        return new Bloodline($query, $parent, $foreignKey, $localKey);
191
    }
192
193
    /**
194
     * Get the model's children.
195
     *
196
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
197
     */
198 5
    public function children()
199
    {
200 5
        return $this->hasMany(static::class, $this->getParentKeyName(), $this->getLocalKeyName());
0 ignored issues
show
Bug introduced by
The method hasMany() does not exist on Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList. Did you maybe mean hasManyOfDescendantsAndSelf()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

200
        return $this->/** @scrutinizer ignore-call */ hasMany(static::class, $this->getParentKeyName(), $this->getLocalKeyName());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
201
    }
202
203
    /**
204
     * Get the model's children and itself.
205
     *
206
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
207
     */
208 5
    public function childrenAndSelf()
209
    {
210 5
        return $this->descendantsAndSelf()->whereDepth('<=', 1);
0 ignored issues
show
Bug introduced by
The method whereDepth() does not exist on Staudenmeir\LaravelAdjac...t\Relations\Descendants. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
        return $this->descendantsAndSelf()->/** @scrutinizer ignore-call */ whereDepth('<=', 1);
Loading history...
211
    }
212
213
    /**
214
     * Get the model's descendants.
215
     *
216
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
217
     */
218 116
    public function descendants()
219
    {
220 116
        return $this->newDescendants(
221 116
            (new static())->newQuery(),
222 116
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...yList::newDescendants(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

222
            /** @scrutinizer ignore-type */ $this,
Loading history...
223 116
            $this->getQualifiedParentKeyName(),
224 116
            $this->getLocalKeyName(),
225 116
            false
226 116
        );
227
    }
228
229
    /**
230
     * Get the model's descendants and itself.
231
     *
232
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
233
     */
234 50
    public function descendantsAndSelf()
235
    {
236 50
        return $this->newDescendants(
237 50
            (new static())->newQuery(),
238 50
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...yList::newDescendants(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

238
            /** @scrutinizer ignore-type */ $this,
Loading history...
239 50
            $this->getQualifiedParentKeyName(),
240 50
            $this->getLocalKeyName(),
241 50
            true
242 50
        );
243
    }
244
245
    /**
246
     * Instantiate a new Descendants relationship.
247
     *
248
     * @param \Illuminate\Database\Eloquent\Builder $query
249
     * @param \Illuminate\Database\Eloquent\Model $parent
250
     * @param string $foreignKey
251
     * @param string $localKey
252
     * @param bool $andSelf
253
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
254
     */
255 166
    protected function newDescendants(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
256
    {
257 166
        return new Descendants($query, $parent, $foreignKey, $localKey, $andSelf);
258
    }
259
260
    /**
261
     * Get the model's parent.
262
     *
263
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
264
     */
265 5
    public function parent()
266
    {
267 5
        return $this->belongsTo(static::class, $this->getParentKeyName(), $this->getLocalKeyName());
0 ignored issues
show
Bug introduced by
The method belongsTo() does not exist on Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList. Did you maybe mean belongsToManyOfDescendantsAndSelf()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

267
        return $this->/** @scrutinizer ignore-call */ belongsTo(static::class, $this->getParentKeyName(), $this->getLocalKeyName());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
268
    }
269
270
    /**
271
     * Get the model's parent and itself.
272
     *
273
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
274
     */
275 5
    public function parentAndSelf()
276
    {
277 5
        return $this->ancestorsAndSelf()->whereDepth('>=', -1);
0 ignored issues
show
Bug introduced by
The method whereDepth() does not exist on Staudenmeir\LaravelAdjac...ent\Relations\Ancestors. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

277
        return $this->ancestorsAndSelf()->/** @scrutinizer ignore-call */ whereDepth('>=', -1);
Loading history...
278
    }
279
280
    /**
281
     * Get the model's root ancestor.
282
     *
283
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor
284
     */
285 25
    public function rootAncestor()
286
    {
287 25
        return $this->newRootAncestor(
288 25
            (new static())->newQuery(),
289 25
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...List::newRootAncestor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
            /** @scrutinizer ignore-type */ $this,
Loading history...
290 25
            $this->getQualifiedParentKeyName(),
291 25
            $this->getLocalKeyName()
292 25
        );
293
    }
294
295
    /**
296
     * Instantiate a new RootAncestor relationship.
297
     *
298
     * @param \Illuminate\Database\Eloquent\Builder $query
299
     * @param \Illuminate\Database\Eloquent\Model $parent
300
     * @param string $foreignKey
301
     * @param string $localKey
302
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor
303
     */
304 25
    protected function newRootAncestor(Builder $query, Model $parent, $foreignKey, $localKey)
305
    {
306 25
        return new RootAncestor($query, $parent, $foreignKey, $localKey);
307
    }
308
309
    /**
310
     * Get the model's siblings.
311
     *
312
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings
313
     */
314 35
    public function siblings()
315
    {
316 35
        return $this->newSiblings(
317 35
            (new static())->newQuery(),
318 35
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...encyList::newSiblings(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

318
            /** @scrutinizer ignore-type */ $this,
Loading history...
319 35
            $this->getQualifiedParentKeyName(),
320 35
            $this->getParentKeyName(),
321 35
            false
322 35
        );
323
    }
324
325
    /**
326
     * Get the model's siblings and itself.
327
     *
328
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings
329
     */
330 30
    public function siblingsAndSelf()
331
    {
332 30
        return $this->newSiblings(
333 30
            (new static())->newQuery(),
334 30
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...Traits\HasAdjacencyList is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...encyList::newSiblings(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

334
            /** @scrutinizer ignore-type */ $this,
Loading history...
335 30
            $this->getQualifiedParentKeyName(),
336 30
            $this->getParentKeyName(),
337 30
            true
338 30
        );
339
    }
340
341
    /**
342
     * Instantiate a new Siblings relationship.
343
     *
344
     * @param \Illuminate\Database\Eloquent\Builder $query
345
     * @param \Illuminate\Database\Eloquent\Model $parent
346
     * @param string $foreignKey
347
     * @param string $localKey
348
     * @param bool $andSelf
349
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings
350
     */
351 65
    protected function newSiblings(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
352
    {
353 65
        return new Siblings($query, $parent, $foreignKey, $localKey, $andSelf);
354
    }
355
356
    /**
357
     * Get the first segment of the model's path.
358
     *
359
     * @return string
360
     */
361 60
    public function getFirstPathSegment()
362
    {
363 60
        $path = $this->attributes[$this->getPathName()];
364
365 60
        return explode($this->getPathSeparator(), $path)[0];
366
    }
367
368
    /**
369
     * Determine whether the model's path is nested.
370
     *
371
     * @return bool
372
     */
373 10
    public function hasNestedPath()
374
    {
375 10
        $path = $this->attributes[$this->getPathName()];
376
377 10
        return Str::contains($path, $this->getPathSeparator());
378
    }
379
380
    /**
381
     * Determine if an attribute is an integer.
382
     *
383
     * @param string $attribute
384
     * @return bool
385
     */
386 132
    public function isIntegerAttribute($attribute)
387
    {
388 132
        $segments = explode('.', $attribute);
389 132
        $attribute = end($segments);
390
391 132
        $casts = $this->getCasts();
0 ignored issues
show
Bug introduced by
It seems like getCasts() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

391
        /** @scrutinizer ignore-call */ 
392
        $casts = $this->getCasts();
Loading history...
392
393 132
        return isset($casts[$attribute]) && in_array($casts[$attribute], ['int', 'integer']);
394
    }
395
396
    /**
397
     * Create a new Eloquent query builder for the model.
398
     *
399
     * @param \Illuminate\Database\Query\Builder $query
400
     * @return \Illuminate\Database\Eloquent\Builder|static
401
     */
402 870
    public function newEloquentBuilder($query)
403
    {
404 870
        return new \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder($query);
405
    }
406
407
    /**
408
     * Create a new Eloquent Collection instance.
409
     *
410
     * @param array $models
411
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection
412
     */
413 835
    public function newCollection(array $models = [])
414
    {
415 835
        return new Collection($models);
0 ignored issues
show
Bug introduced by
$models of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Staudenmeir\LaravelAdjac...llection::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

415
        return new Collection(/** @scrutinizer ignore-type */ $models);
Loading history...
416
    }
417
418
    /**
419
     * Set an additional constraint for the recursive query.
420
     *
421
     * @param callable $constraint
422
     * @return void
423
     */
424 5
    public static function setRecursiveQueryConstraint(callable $constraint)
425
    {
426 5
        static::$recursiveQueryConstraint = $constraint;
427
    }
428
429
    /**
430
     * Unset the additional constraint for the recursive query.
431
     *
432
     * @return void
433
     */
434 5
    public static function unsetRecursiveQueryConstraint()
435
    {
436 5
        static::$recursiveQueryConstraint = null;
437
    }
438
439
    /**
440
     * Execute a query with an additional constraint for the recursive query.
441
     *
442
     * @param callable $constraint
443
     * @param callable $query
444
     * @return mixed
445
     */
446 15
    public static function withRecursiveQueryConstraint(callable $constraint, callable $query)
447
    {
448 15
        $previous = static::$recursiveQueryConstraint;
449
450 15
        static::$recursiveQueryConstraint = $constraint;
451
452 15
        $result = $query();
453
454 15
        static::$recursiveQueryConstraint = $previous;
455
456 15
        return $result;
457
    }
458
459
    /**
460
     * Execute a query with a maximum depth constraint for the recursive query.
461
     *
462
     * @param int $maxDepth
463
     * @param callable $query
464
     * @return mixed
465
     */
466 10
    public static function withMaxDepth(int $maxDepth, callable $query): mixed
467
    {
468 10
        $operator = $maxDepth > 0 ? '<' : '>';
469
470 10
        return static::withRecursiveQueryConstraint(
471 10
            fn (Builder $query) => $query->whereDepth($operator, $maxDepth),
472 10
            $query
473 10
        );
474
    }
475
}
476