Issues (97)

src/Relations/Traits/IsJsonRelation.php (1 issue)

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

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