Passed
Push — master ( 575ac1...7d6027 )
by Jonas
01:44
created

HasManyJson::matchOneOrMany()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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