Passed
Push — master ( 1c1eeb...842078 )
by Jonas
03:25
created

getRelationExistenceQueryForSelfRelation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

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