Passed
Push — 1.10 ( d871de...3d7a0a )
by Jonas
13:32
created

IsJsonRelation::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 1
rs 10
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\SQLiteGrammar;
15
use Staudenmeir\EloquentJsonRelations\Grammars\SqlServerGrammar;
16
17
trait IsJsonRelation
18
{
19
    /**
20
     * The base path of the foreign key.
21
     *
22
     * @var string
23
     */
24
    protected $path;
25
26
    /**
27
     * The optional object key of the foreign key.
28
     *
29
     * @var string
30
     */
31
    protected $key;
32
33
    /**
34
     * Hydrate the pivot relationship on the models.
35
     *
36
     * @param \Illuminate\Database\Eloquent\Collection $models
37
     * @param \Illuminate\Database\Eloquent\Model $parent
38
     * @param callable $callback
39
     * @return \Illuminate\Database\Eloquent\Collection
40
     */
41
    public function hydratePivotRelation(Collection $models, Model $parent, callable $callback): Collection
42
    {
43
        foreach ($models as $i => $model) {
44
            $clone = clone $model;
45
46
            $models[$i] = $clone->setRelation(
47
                $this->getPivotAccessor(),
48
                $this->pivotRelation($clone, $parent, $callback)
49
            );
50
        }
51
52
        return $models;
53
    }
54
55
    /**
56
     * Get the pivot relationship from the query.
57
     *
58
     * @param \Illuminate\Database\Eloquent\Model $model
59
     * @param \Illuminate\Database\Eloquent\Model $parent
60
     * @param callable $callback
61
     * @return \Illuminate\Database\Eloquent\Model
62
     */
63
    protected function pivotRelation(Model $model, Model $parent, callable $callback)
64
    {
65
        $records = $callback($model, $parent);
66
67
        if ($records instanceof Arrayable) {
68
            $records = $records->toArray();
69
        }
70
71
        $attributes = $this->pivotAttributes($model, $parent, $records);
72
73
        return Pivot::fromAttributes($model, $attributes, null, true);
74
    }
75
76
    /**
77
     * Get the pivot attributes from a model.
78
     *
79
     * @param \Illuminate\Database\Eloquent\Model $model
80
     * @param \Illuminate\Database\Eloquent\Model $parent
81
     * @param array $records
82
     * @return array
83
     */
84
    abstract public function pivotAttributes(Model $model, Model $parent, array $records);
85
86
    /**
87
     * Execute the query and get the first related model.
88
     *
89
     * @param array $columns
90
     * @return mixed
91
     */
92
    public function first($columns = ['*'])
93
    {
94
        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

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