Passed
Push — master ( 2ebb51...c237bf )
by Jonas
05:46
created

HasManyJson::parentKeyToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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