Passed
Push — master ( 92ce5e...e90b8a )
by Jonas
02:07 queued 13s
created

HasManyDeep::match()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 3
dl 0
loc 15
ccs 8
cts 8
cp 1
crap 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Staudenmeir\EloquentHasManyDeep;
4
5
use Closure;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
9
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\ExecutesQueries;
10
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\HasEagerLimit;
11
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\HasEagerLoading;
12
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\HasExistenceQueries;
13
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\IsConcatenable;
14
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\JoinsThroughParents;
15
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\RetrievesIntermediateTables;
16
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\SupportsCompositeKeys;
17
use Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits\IsCustomizable;
18
use Staudenmeir\EloquentHasManyDeepContracts\Interfaces\ConcatenableRelation;
19
20
/**
21
 * @template TRelatedModel of \Illuminate\Database\Eloquent\Model
22
 * @extends \Illuminate\Database\Eloquent\Relations\Relation<TRelatedModel>
23
 */
24
class HasManyDeep extends HasManyThrough implements ConcatenableRelation
25
{
26
    use ExecutesQueries;
27
    use HasEagerLimit;
0 ignored issues
show
Bug introduced by
The trait Staudenmeir\EloquentHasM...ns\Traits\HasEagerLimit requires the property $exists which is not provided by Staudenmeir\EloquentHasManyDeep\HasManyDeep.
Loading history...
28
    use HasEagerLoading;
29
    use HasExistenceQueries;
0 ignored issues
show
Bug introduced by
The trait Staudenmeir\EloquentHasM...its\HasExistenceQueries requires the property $from which is not provided by Staudenmeir\EloquentHasManyDeep\HasManyDeep.
Loading history...
30
    use IsConcatenable;
31
    use IsCustomizable;
32
    use JoinsThroughParents;
0 ignored issues
show
Bug introduced by
The trait Staudenmeir\EloquentHasM...its\JoinsThroughParents requires the property $columns which is not provided by Staudenmeir\EloquentHasManyDeep\HasManyDeep.
Loading history...
33
    use RetrievesIntermediateTables;
34
    use SupportsCompositeKeys;
0 ignored issues
show
Bug introduced by
The trait Staudenmeir\EloquentHasM...s\SupportsCompositeKeys requires the property $columns which is not provided by Staudenmeir\EloquentHasManyDeep\HasManyDeep.
Loading history...
35
36
    /**
37
     * The "through" parent model instances.
38
     *
39
     * @var \Illuminate\Database\Eloquent\Model[]
40
     */
41
    protected $throughParents;
42
43
    /**
44
     * The foreign keys on the relationship.
45
     *
46
     * @var array
47
     */
48
    protected $foreignKeys;
49
50
    /**
51
     * The local keys on the relationship.
52
     *
53
     * @var array
54
     */
55
    protected $localKeys;
56
57
    /**
58
     * Create a new has many deep relationship instance.
59
     *
60
     * @param \Illuminate\Database\Eloquent\Builder $query
61
     * @param \Illuminate\Database\Eloquent\Model $farParent
62
     * @param \Illuminate\Database\Eloquent\Model[] $throughParents
63
     * @param array $foreignKeys
64
     * @param array $localKeys
65
     * @return void
66
     */
67 188
    public function __construct(Builder $query, Model $farParent, array $throughParents, array $foreignKeys, array $localKeys)
68
    {
69 188
        $this->throughParents = $throughParents;
70 188
        $this->foreignKeys = $foreignKeys;
71 188
        $this->localKeys = $localKeys;
72
73 188
        $firstKey = is_array($foreignKeys[0])
74 10
            ? $foreignKeys[0][1]
75 178
            : ($this->hasLeadingCompositeKey() ? $foreignKeys[0]->columns[0] : $foreignKeys[0]);
76
77 188
        $localKey = $this->hasLeadingCompositeKey() ? $localKeys[0]->columns[0] : $localKeys[0];
78
79 188
        parent::__construct($query, $farParent, $throughParents[0], $firstKey, $foreignKeys[1], $localKey, $localKeys[1]);
80
    }
81
82
    /**
83
     * Set the base constraints on the relation query.
84
     *
85
     * @return void
86
     */
87 188
    public function addConstraints()
88
    {
89 188
        if ($this->firstKey instanceof Closure || $this->localKey instanceof Closure) {
0 ignored issues
show
introduced by
$this->localKey is never a sub-type of Closure.
Loading history...
90 37
            $this->performJoin();
91
        } else {
92 151
            parent::addConstraints();
93
        }
94
95 188
        if (static::$constraints) {
96 112
            if ($this->firstKey instanceof Closure) {
0 ignored issues
show
introduced by
$this->firstKey is never a sub-type of Closure.
Loading history...
97 11
                ($this->firstKey)($this->query);
98 101
            } elseif ($this->localKey instanceof Closure) {
0 ignored issues
show
introduced by
$this->localKey is never a sub-type of Closure.
Loading history...
99 2
                ($this->localKey)($this->query);
100 99
            } elseif (is_array($this->foreignKeys[0])) {
101 4
                $this->query->where(
102 4
                    $this->throughParent->qualifyColumn($this->foreignKeys[0][0]),
103 4
                    '=',
104 4
                    $this->farParent->getMorphClass()
105 4
                );
106 95
            } elseif ($this->hasLeadingCompositeKey()) {
107 8
                $this->addConstraintsWithCompositeKey();
108
            }
109
        }
110
    }
111
112
    /**
113
     * Set the join clauses on the query.
114
     *
115
     * @param \Illuminate\Database\Eloquent\Builder|null $query
116
     * @return void
117
     */
118 188
    protected function performJoin(Builder $query = null)
119
    {
120 188
        $query = $query ?: $this->query;
121
122 188
        $throughParents = array_reverse($this->throughParents);
123 188
        $foreignKeys = array_reverse($this->foreignKeys);
124 188
        $localKeys = array_reverse($this->localKeys);
125
126 188
        $segments = explode(' as ', $query->getQuery()->from);
127
128 188
        $alias = $segments[1] ?? null;
129
130 188
        foreach ($throughParents as $i => $throughParent) {
131 188
            $predecessor = $throughParents[$i - 1] ?? $this->related;
132
133 188
            $prefix = $i === 0 && $alias ? $alias.'.' : '';
134
135 188
            $this->joinThroughParent($query, $throughParent, $predecessor, $foreignKeys[$i], $localKeys[$i], $prefix);
136
        }
137
    }
138
139
    /**
140
     * Set the select clause for the relation query.
141
     *
142
     * @param array $columns
143
     * @return array
144
     */
145 151
    protected function shouldSelect(array $columns = ['*'])
146
    {
147 151
        if ($columns == ['*']) {
148 151
            $columns = [$this->related->getTable().'.*'];
149
        }
150
151 151
        $alias = 'laravel_through_key';
152
153 151
        if ($this->customThroughKeyCallback) {
154 23
            $columns[] = ($this->customThroughKeyCallback)($alias);
155
        } else {
156 128
            $columns[] = $this->getQualifiedFirstKeyName() . " as $alias";
157
        }
158
159 151
        if ($this->hasLeadingCompositeKey()) {
160 12
            $columns = array_merge(
161 12
                $columns,
162 12
                $this->shouldSelectWithCompositeKey()
163 12
            );
164
        }
165
166 151
        return array_merge($columns, $this->intermediateColumns());
167
    }
168
169
    /**
170
     * Restore soft-deleted models.
171
     *
172
     * @param array|string ...$columns
173
     * @return $this
174
     */
175 14
    public function withTrashed(...$columns)
176
    {
177 14
        if (empty($columns)) {
178 4
            $this->query->withTrashed();
179
180 4
            return $this;
181
        }
182
183 10
        if (is_array($columns[0])) {
184 2
            $columns = $columns[0];
185
        }
186
187 10
        foreach ($columns as $column) {
188 10
            $this->query->withoutGlobalScope(__CLASS__ . ":$column");
189
        }
190
191 10
        return $this;
192
    }
193
194
    /**
195
     * Get the far parent model instance.
196
     *
197
     * @return \Illuminate\Database\Eloquent\Model
198
     */
199 10
    public function getFarParent(): Model
200
    {
201 10
        return $this->farParent;
202
    }
203
204
    /**
205
     * Get the "through" parent model instances.
206
     *
207
     * @return \Illuminate\Database\Eloquent\Model[]
208
     */
209 10
    public function getThroughParents()
210
    {
211 10
        return $this->throughParents;
212
    }
213
214
    /**
215
     * Get the foreign keys on the relationship.
216
     *
217
     * @return array
218
     */
219 10
    public function getForeignKeys()
220
    {
221 10
        return $this->foreignKeys;
222
    }
223
224
    /**
225
     * Get the local keys on the relationship.
226
     *
227
     * @return array
228
     */
229 10
    public function getLocalKeys()
230
    {
231 10
        return $this->localKeys;
232
    }
233
}
234