Passed
Push — master ( 4e5334...4eb7e1 )
by Jonas
04:13
created

HasRecursiveRelationships   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 402
Duplicated Lines 0 %

Test Coverage

Coverage 98.02%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 34
eloc 84
c 2
b 0
f 0
dl 0
loc 402
ccs 99
cts 101
cp 0.9802
rs 9.68

30 Methods

Rating   Name   Duplication   Size   Complexity  
A hasManyOfDescendantsAndSelf() 0 14 3
A getQualifiedLocalKeyName() 0 3 1
A newHasManyOfDescendants() 0 3 1
A getLocalKeyName() 0 3 1
A hasManyOfDescendants() 0 14 3
A rootAncestor() 0 7 1
A getCustomPaths() 0 3 1
A newRootAncestor() 0 3 1
A siblings() 0 8 1
A descendants() 0 8 1
A getFirstPathSegment() 0 5 1
A getQualifiedParentKeyName() 0 3 1
A siblingsAndSelf() 0 8 1
A getPathName() 0 3 1
A newDescendants() 0 3 1
A descendantsAndSelf() 0 8 1
A newSiblings() 0 3 1
A getExpressionName() 0 3 1
A newEloquentBuilder() 0 3 1
A newAncestors() 0 3 1
A parentAndSelf() 0 3 1
A getPathSeparator() 0 3 1
A ancestors() 0 8 1
A children() 0 3 1
A ancestorsAndSelf() 0 8 1
A parent() 0 3 1
A hasNestedPath() 0 5 1
A getDepthName() 0 3 1
A childrenAndSelf() 0 3 1
A getParentKeyName() 0 3 1
1
<?php
2
3
namespace Staudenmeir\LaravelAdjacencyList\Eloquent;
4
5
use Illuminate\Database\Eloquent\Builder;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Staudenmeir\LaravelAdjacencyList\Eloquent\Builder. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Str;
8
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors;
9
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants;
10
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\HasManyOfDescendants;
11
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor;
12
use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings;
13
use Staudenmeir\LaravelCte\Eloquent\QueriesExpressions;
14
15
trait HasRecursiveRelationships
16
{
17
    use HasRecursiveRelationshipScopes, QueriesExpressions;
18
19
    /**
20
     * Get the name of the parent key column.
21
     *
22
     * @return string
23
     */
24 282
    public function getParentKeyName()
25
    {
26 282
        return 'parent_id';
27
    }
28
29
    /**
30
     * Get the qualified parent key column.
31
     *
32
     * @return string
33
     */
34 188
    public function getQualifiedParentKeyName()
35
    {
36 188
        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

36
        return (new static)->/** @scrutinizer ignore-call */ getTable().'.'.$this->getParentKeyName();
Loading history...
37
    }
38
39
    /**
40
     * Get the name of the local key column.
41
     *
42
     * @return string
43
     */
44 254
    public function getLocalKeyName()
45
    {
46 254
        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

46
        return $this->/** @scrutinizer ignore-call */ getKeyName();
Loading history...
47
    }
48
49
    /**
50
     * Get the qualified local key column.
51
     *
52
     * @return string
53
     */
54 190
    public function getQualifiedLocalKeyName()
55
    {
56 190
        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

56
        return $this->/** @scrutinizer ignore-call */ qualifyColumn($this->getLocalKeyName());
Loading history...
57
    }
58
59
    /**
60
     * Get the name of the depth column.
61
     *
62
     * @return string
63
     */
64 168
    public function getDepthName()
65
    {
66 168
        return 'depth';
67
    }
68
69
    /**
70
     * Get the name of the path column.
71
     *
72
     * @return string
73
     */
74 208
    public function getPathName()
75
    {
76 208
        return 'path';
77
    }
78
79
    /**
80
     * Get the path separator.
81
     *
82
     * @return string
83
     */
84 184
    public function getPathSeparator()
85
    {
86 184
        return '.';
87
    }
88
89
    /**
90
     * Get the additional custom paths.
91
     *
92
     * @return array
93
     */
94
    public function getCustomPaths()
95
    {
96
        return [];
97
    }
98
99
    /**
100
     * Get the name of the common table expression.
101
     *
102
     * @return string
103
     */
104 214
    public function getExpressionName()
105
    {
106 214
        return 'laravel_cte';
107
    }
108
    
109
    /**
110
     * Get the model's ancestors.
111
     *
112
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
113
     */
114 38
    public function ancestors()
115
    {
116 38
        return $this->newAncestors(
117 38
            (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

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

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

118
            /** @scrutinizer ignore-type */ $this,
Loading history...
119 38
            $this->getQualifiedParentKeyName(),
120 38
            $this->getLocalKeyName(),
121 38
            false
122
        );
123
    }
124
125
    /**
126
     * Get the model's ancestors and itself.
127
     *
128
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
129
     */
130 26
    public function ancestorsAndSelf()
131
    {
132 26
        return $this->newAncestors(
133 26
            (new static)->newQuery(),
134
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...onships::newAncestors(). ( Ignorable by Annotation )

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

134
            /** @scrutinizer ignore-type */ $this,
Loading history...
135 26
            $this->getQualifiedParentKeyName(),
136 26
            $this->getLocalKeyName(),
137 26
            true
138
        );
139
    }
140
141
    /**
142
     * Instantiate a new Ancestors relationship.
143
     *
144
     * @param \Illuminate\Database\Eloquent\Builder $query
145
     * @param \Illuminate\Database\Eloquent\Model $parent
146
     * @param string $foreignKey
147
     * @param string $localKey
148
     * @param bool $andSelf
149
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
150
     */
151 64
    protected function newAncestors(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
152
    {
153 64
        return new Ancestors($query, $parent, $foreignKey, $localKey, $andSelf);
154
    }
155
156
    /**
157
     * Get the model's children.
158
     *
159
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
160
     */
161 4
    public function children()
162
    {
163 4
        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...sRecursiveRelationships. 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

163
        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...
164
    }
165
166
    /**
167
     * Get the model's children and itself.
168
     *
169
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
170
     */
171 4
    public function childrenAndSelf()
172
    {
173 4
        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

173
        return $this->descendantsAndSelf()->/** @scrutinizer ignore-call */ whereDepth('<=', 1);
Loading history...
174
    }
175
176
    /**
177
     * Get the model's descendants.
178
     *
179
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
180
     */
181 47
    public function descendants()
182
    {
183 47
        return $this->newDescendants(
184 47
            (new static)->newQuery(),
185
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ships::newDescendants(). ( Ignorable by Annotation )

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

185
            /** @scrutinizer ignore-type */ $this,
Loading history...
186 47
            $this->getQualifiedParentKeyName(),
187 47
            $this->getLocalKeyName(),
188 47
            false
189
        );
190
    }
191
192
    /**
193
     * Get the model's descendants and itself.
194
     *
195
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
196
     */
197 22
    public function descendantsAndSelf()
198
    {
199 22
        return $this->newDescendants(
200 22
            (new static)->newQuery(),
201
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ships::newDescendants(). ( Ignorable by Annotation )

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

201
            /** @scrutinizer ignore-type */ $this,
Loading history...
202 22
            $this->getQualifiedParentKeyName(),
203 22
            $this->getLocalKeyName(),
204 22
            true
205
        );
206
    }
207
208
    /**
209
     * Instantiate a new Descendants relationship.
210
     *
211
     * @param \Illuminate\Database\Eloquent\Builder $query
212
     * @param \Illuminate\Database\Eloquent\Model $parent
213
     * @param string $foreignKey
214
     * @param string $localKey
215
     * @param bool $andSelf
216
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants
217
     */
218 69
    protected function newDescendants(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
219
    {
220 69
        return new Descendants($query, $parent, $foreignKey, $localKey, $andSelf);
221
    }
222
223
    /**
224
     * Get the model's parent.
225
     *
226
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
227
     */
228 4
    public function parent()
229
    {
230 4
        return $this->belongsTo(static::class, $this->getParentKeyName(), $this->getLocalKeyName());
0 ignored issues
show
Bug introduced by
It seems like belongsTo() 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

230
        return $this->/** @scrutinizer ignore-call */ belongsTo(static::class, $this->getParentKeyName(), $this->getLocalKeyName());
Loading history...
231
    }
232
233
    /**
234
     * Get the model's parent and itself.
235
     *
236
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors
237
     */
238 4
    public function parentAndSelf()
239
    {
240 4
        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

240
        return $this->ancestorsAndSelf()->/** @scrutinizer ignore-call */ whereDepth('>=', -1);
Loading history...
241
    }
242
243
    /**
244
     * Get the model's root ancestor.
245
     *
246
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor
247
     */
248 22
    public function rootAncestor()
249
    {
250 22
        return $this->newRootAncestor(
251 22
            (new static)->newQuery(),
252
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...hips::newRootAncestor(). ( Ignorable by Annotation )

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

252
            /** @scrutinizer ignore-type */ $this,
Loading history...
253 22
            $this->getQualifiedParentKeyName(),
254 22
            $this->getLocalKeyName()
255
        );
256
    }
257
258
    /**
259
     * Instantiate a new RootAncestor relationship.
260
     *
261
     * @param \Illuminate\Database\Eloquent\Builder $query
262
     * @param \Illuminate\Database\Eloquent\Model $parent
263
     * @param string $foreignKey
264
     * @param string $localKey
265
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor
266
     */
267 22
    protected function newRootAncestor(Builder $query, Model $parent, $foreignKey, $localKey)
268
    {
269 22
        return new RootAncestor($query, $parent, $foreignKey, $localKey);
270
    }
271
272
    /**
273
     * Get the model's siblings.
274
     *
275
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings
276
     */
277 28
    public function siblings()
278
    {
279 28
        return $this->newSiblings(
280 28
            (new static)->newQuery(),
281
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ionships::newSiblings(). ( Ignorable by Annotation )

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

281
            /** @scrutinizer ignore-type */ $this,
Loading history...
282 28
            $this->getQualifiedParentKeyName(),
283 28
            $this->getParentKeyName(),
284 28
            false
285
        );
286
    }
287
288
    /**
289
     * Get the model's siblings and itself.
290
     *
291
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings
292
     */
293 24
    public function siblingsAndSelf()
294
    {
295 24
        return $this->newSiblings(
296 24
            (new static)->newQuery(),
297
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...ionships::newSiblings(). ( Ignorable by Annotation )

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

297
            /** @scrutinizer ignore-type */ $this,
Loading history...
298 24
            $this->getQualifiedParentKeyName(),
299 24
            $this->getParentKeyName(),
300 24
            true
301
        );
302
    }
303
304
    /**
305
     * Instantiate a new Siblings relationship.
306
     *
307
     * @param \Illuminate\Database\Eloquent\Builder $query
308
     * @param \Illuminate\Database\Eloquent\Model $parent
309
     * @param string $foreignKey
310
     * @param string $localKey
311
     * @param bool $andSelf
312
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings
313
     */
314 52
    protected function newSiblings(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
315
    {
316 52
        return new Siblings($query, $parent, $foreignKey, $localKey, $andSelf);
317
    }
318
319
    /**
320
     * Define a one-to-many relationship of the model's descendants.
321
     *
322
     * @param string $related
323
     * @param string|null $foreignKey
324
     * @param string|null $localKey
325
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\HasManyOfDescendants
326
     */
327 30
    public function hasManyOfDescendants($related, $foreignKey = null, $localKey = null)
328
    {
329 30
        $instance = $this->newRelatedInstance($related);
0 ignored issues
show
Bug introduced by
It seems like newRelatedInstance() 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

329
        /** @scrutinizer ignore-call */ 
330
        $instance = $this->newRelatedInstance($related);
Loading history...
330
331 30
        $foreignKey = $foreignKey ?: $this->getForeignKey();
0 ignored issues
show
Bug introduced by
It seems like getForeignKey() 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

331
        $foreignKey = $foreignKey ?: $this->/** @scrutinizer ignore-call */ getForeignKey();
Loading history...
332
333 30
        $localKey = $localKey ?: $this->getKeyName();
334
335 30
        return $this->newHasManyOfDescendants(
336 30
            $instance->newQuery(),
337
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...wHasManyOfDescendants(). ( Ignorable by Annotation )

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

337
            /** @scrutinizer ignore-type */ $this,
Loading history...
338 30
            $instance->qualifyColumn($foreignKey),
339
            $localKey,
340 30
            false
341
        );
342
    }
343
344
    /**
345
     * Define a one-to-many relationship of the model's descendants and itself.
346
     *
347
     * @param string $related
348
     * @param string|null $foreignKey
349
     * @param string|null $localKey
350
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\HasManyOfDescendants
351
     */
352 22
    public function hasManyOfDescendantsAndSelf($related, $foreignKey = null, $localKey = null)
353
    {
354 22
        $instance = $this->newRelatedInstance($related);
355
356 22
        $foreignKey = $foreignKey ?: $this->getForeignKey();
357
358 22
        $localKey = $localKey ?: $this->getKeyName();
359
360 22
        return $this->newHasManyOfDescendants(
361 22
            $instance->newQuery(),
362
            $this,
0 ignored issues
show
Bug introduced by
$this of type Staudenmeir\LaravelAdjac...sRecursiveRelationships is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $parent of Staudenmeir\LaravelAdjac...wHasManyOfDescendants(). ( Ignorable by Annotation )

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

362
            /** @scrutinizer ignore-type */ $this,
Loading history...
363 22
            $instance->qualifyColumn($foreignKey),
364
            $localKey,
365 22
            true
366
        );
367
    }
368
369
    /**
370
     * Instantiate a new HasManyOfDescendants relationship.
371
     *
372
     * @param \Illuminate\Database\Eloquent\Builder $query
373
     * @param \Illuminate\Database\Eloquent\Model $parent
374
     * @param string $foreignKey
375
     * @param string $localKey
376
     * @param bool $andSelf
377
     * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\HasManyOfDescendants
378
     */
379 52
    protected function newHasManyOfDescendants(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf)
380
    {
381 52
        return new HasManyOfDescendants($query, $parent, $foreignKey, $localKey, $andSelf);
382
    }
383
384
    /**
385
     * Get the first segment of the model's path.
386
     *
387
     * @return string
388
     */
389 40
    public function getFirstPathSegment()
390
    {
391 40
        $path = $this->attributes[$this->getPathName()];
392
393 40
        return explode($this->getPathSeparator(), $path)[0];
394
    }
395
396
    /**
397
     * Determine whether the model's path is nested.
398
     *
399
     * @return bool
400
     */
401 8
    public function hasNestedPath()
402
    {
403 8
        $path = $this->attributes[$this->getPathName()];
404
405 8
        return Str::contains($path, $this->getPathSeparator());
406
    }
407
408
    /**
409
     * Create a new Eloquent query builder for the model.
410
     *
411
     * @param \Illuminate\Database\Query\Builder $query
412
     * @return \Illuminate\Database\Eloquent\Builder|static
413
     */
414 282
    public function newEloquentBuilder($query)
415
    {
416 282
        return new \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder($query);
417
    }
418
}
419