Passed
Push — master ( 09af0a...9826c5 )
by Jonas
06:00
created

BelongsToJson::relationExistenceQueryOwnerKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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