Passed
Push — master ( 8ae916...9ba23c )
by Jonas
12:36
created

IsJsonRelation::getJsonGrammar()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.25

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 1
dl 0
loc 14
ccs 6
cts 8
cp 0.75
crap 4.25
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace Staudenmeir\EloquentJsonRelations\Relations\Traits;
4
5
use Illuminate\Contracts\Database\Query\Builder;
6
use Illuminate\Contracts\Support\Arrayable;
7
use Illuminate\Database\Eloquent\Collection;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\Pivot;
10
use RuntimeException;
11
use Staudenmeir\EloquentJsonRelations\Grammars\JsonGrammar;
12
use Staudenmeir\EloquentJsonRelations\Grammars\MySqlGrammar;
13
use Staudenmeir\EloquentJsonRelations\Grammars\PostgresGrammar;
14
use Staudenmeir\EloquentJsonRelations\Grammars\SqlServerGrammar;
15
16
trait IsJsonRelation
17
{
18
    /**
19
     * The base path of the foreign key.
20
     *
21
     * @var string
22
     */
23
    protected $path;
24
25
    /**
26
     * The optional object key of the foreign key.
27
     *
28
     * @var string
29
     */
30
    protected $key;
31
32
    /**
33
     * Create a new JSON relationship instance.
34
     *
35
     * @return void
36
     */
37 363
    public function __construct()
38
    {
39 363
        $args = func_get_args();
40
41 363
        $foreignKey = explode('[]->', $args[2]);
42
43 363
        $this->path = $foreignKey[0];
44 363
        $this->key = $foreignKey[1] ?? null;
45
46 363
        parent::__construct(...$args);
47
    }
48
49
    /**
50
     * Hydrate the pivot relationship on the models.
51
     *
52
     * @param \Illuminate\Database\Eloquent\Collection $models
53
     * @param \Illuminate\Database\Eloquent\Model $parent
54
     * @param callable $callback
55
     * @return void
56
     */
57 81
    public function hydratePivotRelation(Collection $models, Model $parent, callable $callback)
58
    {
59 81
        foreach ($models as $i => $model) {
60 81
            $clone = clone $model;
61
62 81
            $models[$i] = $clone->setRelation(
63 81
                $this->getPivotAccessor(),
64 81
                $this->pivotRelation($clone, $parent, $callback)
65 81
            );
66
        }
67
    }
68
69
    /**
70
     * Get the pivot relationship from the query.
71
     *
72
     * @param \Illuminate\Database\Eloquent\Model $model
73
     * @param \Illuminate\Database\Eloquent\Model $parent
74
     * @param callable $callback
75
     * @return \Illuminate\Database\Eloquent\Model
76
     */
77 81
    protected function pivotRelation(Model $model, Model $parent, callable $callback)
78
    {
79 81
        $records = $callback($model, $parent);
80
81 81
        if ($records instanceof Arrayable) {
82 9
            $records = $records->toArray();
83
        }
84
85 81
        $attributes = $this->pivotAttributes($model, $parent, $records);
86
87 81
        return Pivot::fromAttributes($model, $attributes, null, true);
88
    }
89
90
    /**
91
     * Get the pivot attributes from a model.
92
     *
93
     * @param \Illuminate\Database\Eloquent\Model $model
94
     * @param \Illuminate\Database\Eloquent\Model $parent
95
     * @param array $records
96
     * @return array
97
     */
98
    abstract public function pivotAttributes(Model $model, Model $parent, array $records);
99
100
    /**
101
     * Execute the query and get the first related model.
102
     *
103
     * @param array $columns
104
     * @return mixed
105
     */
106 6
    public function first($columns = ['*'])
107
    {
108 6
        return $this->take(1)->get($columns)->first();
0 ignored issues
show
Bug introduced by
It seems like take() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

108
        return $this->/** @scrutinizer ignore-call */ take(1)->get($columns)->first();
Loading history...
109
    }
110
111
    /**
112
     * Get the fully qualified path of the relationship.
113
     *
114
     * @return string
115
     */
116 141
    public function getQualifiedPath()
117
    {
118 141
        return $this->parent->qualifyColumn($this->path);
119
    }
120
121
    /**
122
     * Add a “where JSON contains” or "member of" clause to the query.
123
     *
124
     * @param \Illuminate\Contracts\Database\Query\Builder $query
125
     * @param string $column
126
     * @param mixed $value
127
     * @param callable|null $objectValueCallback
128
     * @param string $boolean
129
     * @return void
130
     */
131 231
    protected function whereJsonContainsOrMemberOf(
132
        Builder $query,
133
        string $column,
134
        mixed $value,
135
        callable $objectValueCallback = null,
136
        string $boolean = 'and'
137
    ): void {
138 231
        $grammar = $this->getJsonGrammar($query);
139 231
        $connection = $query->getConnection();
140
141 231
        if ($grammar->supportsMemberOf($connection)) {
142 77
            $query->whereRaw(
143 77
                $grammar->compileMemberOf($column, $this->key, $value),
144 77
                $grammar->prepareBindingsForMemberOf($value),
145 77
                $boolean
146 77
            );
147
        } else {
148 154
            $value = $this->key && $objectValueCallback ? $objectValueCallback($value) : $value;
149
150 154
            $query->whereJsonContains($column, $value, $boolean);
151
        }
152
    }
153
154
    /**
155
     * Get the JSON grammar.
156
     *
157
     * @param \Illuminate\Contracts\Database\Query\Builder $query
158
     * @return \Staudenmeir\EloquentJsonRelations\Grammars\JsonGrammar
159
     */
160 231
    protected function getJsonGrammar(Builder $query): JsonGrammar
161
    {
162 231
        $driver = $query->getConnection()->getDriverName();
163
164
        switch ($driver) {
165 231
            case 'mysql':
166 154
                return $query->getConnection()->withTablePrefix(new MySqlGrammar());
167 77
            case 'pgsql':
168 77
                return $query->getConnection()->withTablePrefix(new PostgresGrammar());
169
            case 'sqlsrv':
170
                return $query->getConnection()->withTablePrefix(new SqlServerGrammar());
171
        }
172
173
        throw new RuntimeException('This database is not supported.'); // @codeCoverageIgnore
174
    }
175
176
    /**
177
     * Get the name of the pivot accessor for this relationship.
178
     *
179
     * @return string
180
     */
181 81
    public function getPivotAccessor(): string
182
    {
183 81
        return 'pivot';
184
    }
185
186
    /**
187
     * Get the base path of the foreign key.
188
     *
189
     * @return string
190
     */
191 9
    public function getForeignKeyPath(): string
192
    {
193 9
        return $this->path;
194
    }
195
}
196