Passed
Push — master ( 7b0a8b...1c1eeb )
by Jonas
02:58
created

BelongsToJson::getArrayableItems()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.675

Importance

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