Passed
Pull Request — master (#97)
by
unknown
12:49
created

BelongsToJson::getRelatedKeyName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
3
namespace Staudenmeir\EloquentJsonRelations\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\BelongsTo;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Collection as BaseCollection;
11
use Staudenmeir\EloquentHasManyDeepContracts\Interfaces\ConcatenableRelation;
12
use Staudenmeir\EloquentJsonRelations\Relations\Traits\Concatenation\IsConcatenableBelongsToJsonRelation;
13
use Staudenmeir\EloquentJsonRelations\Relations\Traits\IsJsonRelation;
14
15
class BelongsToJson extends BelongsTo implements ConcatenableRelation
16
{
17
    use InteractsWithPivotRecords;
18
    use IsConcatenableBelongsToJsonRelation;
0 ignored issues
show
introduced by
The trait Staudenmeir\EloquentJson...leBelongsToJsonRelation requires some properties which are not provided by Staudenmeir\EloquentJson...Relations\BelongsToJson: $laravel_through_key, $wheres, $connection
Loading history...
19
    use IsJsonRelation;
20
21
    /**
22
     * Get the results of the relationship.
23
     *
24
     * @return mixed
25
     */
26 72
    public function getResults()
27
    {
28 72
        return !empty($this->getForeignKeys())
29 68
            ? $this->get()
30 72
            : $this->related->newCollection();
31
    }
32
33
    /**
34
     * Execute the query as a "select" statement.
35
     *
36
     * @param array $columns
37
     * @return \Illuminate\Database\Eloquent\Collection
38
     */
39 96
    public function get($columns = ['*'])
40
    {
41 96
        $models = parent::get($columns);
42
43 96
        if ($this->key && !empty($this->parent->{$this->path})) {
44 56
            $this->hydratePivotRelation(
45 56
                $models,
46 56
                $this->parent,
47 56
                fn (Model $model, Model $parent) => $parent->{$this->path}
48 56
            );
49
        }
50
51 96
        return $models;
52
    }
53
54
    /**
55
     * Set the base constraints on the relation query.
56
     *
57
     * @return void
58
     */
59 275
    public function addConstraints()
60
    {
61 275
        if (static::$constraints) {
62 161
            $table = $this->related->getTable();
63
64 161
            $this->query->whereIn($table.'.'.$this->ownerKey, $this->getForeignKeys());
65
        }
66
    }
67
68
    /**
69
     * Gather the keys from an array of related models.
70
     *
71
     * @param array $models
72
     * @return array
73
     */
74 68
    protected function getEagerModelKeys(array $models)
75
    {
76 68
        $keys = [];
77
78 68
        foreach ($models as $model) {
79 68
            $keys = array_merge($keys, $this->getForeignKeys($model));
80
        }
81
82 68
        sort($keys);
83
84 68
        return array_values(array_unique($keys));
85
    }
86
87
    /**
88
     * Match the eagerly loaded results to their parents.
89
     *
90
     * @param array  $models
91
     * @param \Illuminate\Database\Eloquent\Collection $results
92
     * @param string $relation
93
     * @return array
94
     */
95 48
    public function match(array $models, Collection $results, $relation)
96
    {
97 48
        $dictionary = $this->buildDictionary($results);
98
99 48
        foreach ($models as $model) {
100 48
            $matches = [];
101
102 48
            foreach ($this->getForeignKeys($model) as $id) {
103 48
                if (isset($dictionary[$id])) {
104 48
                    $matches[] = $dictionary[$id];
105
                }
106
            }
107
108 48
            $model->setRelation($relation, $collection = $this->related->newCollection($matches));
109
110 48
            if ($this->key) {
111 32
                $this->hydratePivotRelation(
112 32
                    $collection,
113 32
                    $model,
114 32
                    fn (Model $model, Model $parent) => $parent->{$this->path}
115 32
                );
116
            }
117
        }
118
119 48
        return $models;
120
    }
121
122
    /**
123
     * Build model dictionary keyed by the relation's foreign key.
124
     *
125
     * @param \Illuminate\Database\Eloquent\Collection $results
126
     * @return array
127
     */
128 48
    protected function buildDictionary(Collection $results)
129
    {
130 48
        $dictionary = [];
131
132 48
        foreach ($results as $result) {
133 48
            $dictionary[$result->{$this->ownerKey}] = $result;
134
        }
135
136 48
        return $dictionary;
137
    }
138
139
    /**
140
     * Add the constraints for a relationship query.
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 28
    public function getRelationExistenceQuery(Builder $query, Builder $parentQuery, $columns = ['*'])
148
    {
149 28
        if ($parentQuery->getQuery()->from == $query->getQuery()->from) {
150 7
            return $this->getRelationExistenceQueryForSelfRelation($query, $parentQuery, $columns);
151
        }
152
153 21
        [$sql, $bindings] = $this->relationExistenceQueryOwnerKey($query, $this->ownerKey);
154
155 21
        $query->addBinding($bindings);
156
157 21
        $this->whereJsonContainsOrMemberOf(
158 21
            $query,
159 21
            $this->getQualifiedPath(),
160 21
            $query->getQuery()->connection->raw($sql)
161 21
        );
162
163 21
        return $query->select($columns);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->select($columns) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
164
    }
165
166
    /**
167
     * Add the constraints for a relationship query on the same table.
168
     *
169
     * @param \Illuminate\Database\Eloquent\Builder $query
170
     * @param \Illuminate\Database\Eloquent\Builder $parentQuery
171
     * @param array|mixed $columns
172
     * @return \Illuminate\Database\Eloquent\Builder
173
     */
174 7
    public function getRelationExistenceQueryForSelfRelation(Builder $query, Builder $parentQuery, $columns = ['*'])
175
    {
176 7
        $query->from($query->getModel()->getTable().' as '.$hash = $this->getRelationCountHash());
177
178 7
        $query->getModel()->setTable($hash);
179
180 7
        [$sql, $bindings] = $this->relationExistenceQueryOwnerKey($query, $hash.'.'.$this->ownerKey);
181
182 7
        $query->addBinding($bindings);
183
184 7
        $this->whereJsonContainsOrMemberOf(
185 7
            $query,
186 7
            $this->getQualifiedPath(),
187 7
            $query->getQuery()->connection->raw($sql)
188 7
        );
189
190 7
        return $query->select($columns);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->select($columns) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
191
    }
192
193
    /**
194
     * Get the owner key for the relationship query.
195
     *
196
     * @param \Illuminate\Database\Eloquent\Builder $query
197
     * @param string $ownerKey
198
     * @return array
199
     */
200 95
    protected function relationExistenceQueryOwnerKey(Builder $query, string $ownerKey): array
201
    {
202 95
        $ownerKey = $query->qualifyColumn($ownerKey);
203
204 95
        $grammar = $this->getJsonGrammar($query);
205 95
        $connection = $query->getConnection();
206
207 95
        if ($grammar->supportsMemberOf($connection)) {
208 27
            $sql = $grammar->wrap($ownerKey);
209
210 27
            $bindings = [];
211
        } else {
212 68
            if ($this->key) {
213 26
                $keys = explode('->', $this->key);
214
215 26
                $sql = $this->getJsonGrammar($query)->compileJsonObject($ownerKey, count($keys));
216
217 26
                $bindings = $keys;
218
            } else {
219 42
                $sql = $this->getJsonGrammar($query)->compileJsonArray($ownerKey);
220
221 42
                $bindings = [];
222
            }
223
        }
224
225 95
        return [$sql, $bindings];
226
    }
227
228
    /**
229
     * Get the pivot attributes from a model.
230
     *
231
     * @param \Illuminate\Database\Eloquent\Model $model
232
     * @param \Illuminate\Database\Eloquent\Model $parent
233
     * @param array $records
234
     * @return array
235
     */
236 78
    public function pivotAttributes(Model $model, Model $parent, array $records)
0 ignored issues
show
Unused Code introduced by
The parameter $parent is not used and could be removed. ( Ignorable by Annotation )

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

236
    public function pivotAttributes(Model $model, /** @scrutinizer ignore-unused */ Model $parent, array $records)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
237
    {
238 78
        $key = str_replace('->', '.', $this->key);
239
240 78
        $record = (new BaseCollection($records))
0 ignored issues
show
Bug introduced by
$records of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::__construct(). ( Ignorable by Annotation )

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

240
        $record = (new BaseCollection(/** @scrutinizer ignore-type */ $records))
Loading history...
241 78
            ->filter(function ($value) use ($key, $model) {
242 78
                return Arr::get($value, $key) == $model->{$this->ownerKey};
243 78
            })->first();
244
245 78
        return Arr::except($record, $key);
246
    }
247
248
    /**
249
     * Get the foreign key values.
250
     *
251
     * @param \Illuminate\Database\Eloquent\Model|null $model
252
     * @return array
253
     */
254 205
    public function getForeignKeys(Model $model = null)
255
    {
256 205
        $model = $model ?: $this->child;
257
258 205
        return (new BaseCollection($model->{$this->foreignKey}))->filter(fn ($key) => $key !== null)->all();
259
    }
260
261
    /**
262
     * Get the related key for the relationship.
263
     *
264
     * @return string
265
     */
266
    public function getRelatedKeyName()
267
    {
268
        return $this->ownerKey;
269
    }
270
}
271