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

Descendants   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 19
eloc 61
c 2
b 0
f 0
dl 0
loc 175
ccs 69
cts 69
cp 1
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getRelationExistenceQueryForSelfRelation() 0 21 2
A buildDictionary() 0 19 3
A getRelationExistenceQuery() 0 19 3
A addExpression() 0 7 3
A match() 0 15 3
A addEagerConstraints() 0 13 2
A addConstraints() 0 13 3
1
<?php
2
3
namespace Staudenmeir\LaravelAdjacencyList\Eloquent\Relations;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Collection;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\Relations\HasMany;
9
10
class Descendants extends HasMany
11
{
12
    use IsRecursiveRelation {
13
        buildDictionary as buildDictionaryParent;
14
    }
15
16
    /**
17
     * Set the base constraints on the relation query.
18
     *
19
     * @return void
20
     */
21 69
    public function addConstraints()
22
    {
23 69
        if (static::$constraints) {
24 41
            $constraint = function (Builder $query) {
25 41
                if ($this->andSelf) {
26 8
                    $query->where($this->getQualifiedLocalKeyName(), '=', $this->getParentKey());
27
                } else {
28 33
                    $query->where($this->foreignKey, '=', $this->getParentKey())
29 33
                        ->whereNotNull($this->foreignKey);
30
                }
31 41
            };
32
33 41
            $this->addExpression($constraint);
34
        }
35 69
    }
36
37
    /**
38
     * Set the constraints for an eager load of the relation.
39
     *
40
     * @param array $models
41
     * @return void
42
     */
43 16
    public function addEagerConstraints(array $models)
44
    {
45 16
        $whereIn = $this->whereInMethod($this->parent, $this->localKey);
46
47 16
        $column = $this->andSelf ? $this->getQualifiedLocalKeyName() : $this->foreignKey;
48
49 16
        $keys = $this->getKeys($models, $this->localKey);
50
51 16
        $constraint = function (Builder $query) use ($models, $whereIn, $column, $keys) {
0 ignored issues
show
Unused Code introduced by
The import $models is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
52 16
            $query->$whereIn($column, $keys);
53 16
        };
54
55 16
        $this->addExpression($constraint);
56 16
    }
57
58
    /**
59
     * Match the eagerly loaded results to their parents.
60
     *
61
     * @param array $models
62
     * @param \Illuminate\Database\Eloquent\Collection $results
63
     * @param string $relation
64
     * @return array
65
     */
66 16
    public function match(array $models, Collection $results, $relation)
67
    {
68 16
        $dictionary = $this->buildDictionary($results);
69
70 16
        foreach ($models as $model) {
71 16
            $key = $model->{$this->localKey};
72
73 16
            if (isset($dictionary[$key])) {
74 16
                $value = $this->related->newCollection($dictionary[$key]);
75
76 16
                $model->setRelation($relation, $value);
77
            }
78
        }
79
80 16
        return $models;
81
    }
82
83
    /**
84
     * Build model dictionary.
85
     *
86
     * @param \Illuminate\Database\Eloquent\Collection $results
87
     * @return array
88
     */
89 16
    protected function buildDictionary(Collection $results)
90
    {
91 16
        if ($this->andSelf) {
92 8
            return $this->buildDictionaryParent($results);
93
        }
94
95 8
        $dictionary = $results->keyBy($this->localKey);
96
97 8
        $foreignKey = $this->getForeignKeyName();
98
99 8
        return $results->mapToDictionary(function (Model $result) use ($dictionary, $foreignKey) {
100 8
            if ($result->hasNestedPath()) {
101 8
                $key = $dictionary[$result->getFirstPathSegment()]->{$foreignKey};
102
            } else {
103 8
                $key = $result->{$foreignKey};
104
            }
105
106 8
            return [$key => $result];
107 8
        })->all();
108
    }
109
110
    /**
111
     * Add the constraints for a relationship query.
112
     *
113
     * @param \Illuminate\Database\Eloquent\Builder $query
114
     * @param \Illuminate\Database\Eloquent\Builder $parentQuery
115
     * @param array|mixed $columns
116
     * @return \Illuminate\Database\Eloquent\Builder
117
     */
118 12
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
119
    {
120 12
        if ($query->getQuery()->from === $parentQuery->getQuery()->from) {
121 6
            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
122
        }
123
124 6
        $first = $this->andSelf
125 3
            ? $query->getQuery()->from.'.'.$this->localKey
126 6
            : $this->foreignKey;
127
128 6
        $constraint = function (Builder $query) use ($first) {
129 6
            $query->whereColumn(
130 6
                $first,
131 6
                '=',
132 6
                $this->getQualifiedParentKeyName()
133
            );
134 6
        };
135
136 6
        return $this->addExpression($constraint, $query->select($columns));
0 ignored issues
show
Bug introduced by
It seems like $query->select($columns) can also be of type Illuminate\Database\Query\Builder; however, parameter $query of Staudenmeir\LaravelAdjac...ndants::addExpression() does only seem to accept Illuminate\Database\Eloquent\Builder|null, maybe add an additional type check? ( Ignorable by Annotation )

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

136
        return $this->addExpression($constraint, /** @scrutinizer ignore-type */ $query->select($columns));
Loading history...
137
    }
138
139
    /**
140
     * Add the constraints for a relationship query on the same table.
141
     *
142
     * @param \Illuminate\Database\Eloquent\Builder $query
143
     * @param \Illuminate\Database\Eloquent\Builder $parentQuery
144
     * @param array|mixed $columns
145
     * @return \Illuminate\Database\Eloquent\Builder
146
     */
147 6
    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
148
    {
149 6
        $table = $this->getRelationCountHash();
150
151 6
        $from = $query->getModel()->getTable().' as '.$table;
152
153 6
        $query->getModel()->setTable($table);
154
155 6
        $first = $this->andSelf
156 3
            ? $table.'.'.$this->localKey
157 6
            : $table.'.'.$this->getForeignKeyName();
158
159 6
        $constraint = function (Builder $query) use ($first) {
160 6
            $query->whereColumn(
161 6
                $first,
162 6
                '=',
163 6
                $this->getQualifiedParentKeyName()
164
            );
165 6
        };
166
167 6
        return $this->addExpression($constraint, $query->select($columns), $from);
0 ignored issues
show
Bug introduced by
It seems like $query->select($columns) can also be of type Illuminate\Database\Query\Builder; however, parameter $query of Staudenmeir\LaravelAdjac...ndants::addExpression() does only seem to accept Illuminate\Database\Eloquent\Builder|null, maybe add an additional type check? ( Ignorable by Annotation )

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

167
        return $this->addExpression($constraint, /** @scrutinizer ignore-type */ $query->select($columns), $from);
Loading history...
168
    }
169
170
    /**
171
     * Add a recursive expression to the query.
172
     *
173
     * @param callable $constraint
174
     * @param \Illuminate\Database\Eloquent\Builder|null $query
175
     * @param string|null $from
176
     * @return \Illuminate\Database\Eloquent\Builder
177
     */
178 69
    protected function addExpression(callable $constraint, Builder $query = null, $from = null)
179
    {
180 69
        $query = $query ?: $this->query;
181
182 69
        $initialDepth = $this->andSelf ? 0 : 1;
183
184 69
        return $query->withRelationshipExpression('desc', $constraint, $initialDepth, $from);
185
    }
186
}
187