Issues (64)

src/Http/Traits/HasCompositePrimaryKey.php (13 issues)

1
<?php
2
3
namespace MaksimM\CompositePrimaryKeys\Http\Traits;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Arr;
8
use MaksimM\CompositePrimaryKeys\Eloquent\CompositeKeyQueryBuilder;
9
use MaksimM\CompositePrimaryKeys\Exceptions\MissingPrimaryKeyValueException;
10
use MaksimM\CompositePrimaryKeys\Exceptions\WrongKeyException;
11
use MaksimM\CompositePrimaryKeys\Scopes\CompositeKeyScope;
12
13
trait HasCompositePrimaryKey
14
{
15
    use NormalizedKeysParser;
16
    use PrimaryKeyInformation;
17
    use CompositeRelationships;
18
    use OptionalBinaryTransformation;
19
20
    /**
21
     * Automatically generate unique binary id.
22
     */
23
    public static function bootHasCompositePrimaryKey()
24
    {
25
        static::creating(function ($model) {
26
            foreach ($model->getRawKeyName() as $key) {
27
                if ((!isset($model->{$key}) || empty($model->{$key})) && in_array($key, $model->getBinaryColumns())) {
28
                    $v = uniqid(rand(), true);
29
                    $model->{$key} = $model->hexBinaryColumns() ? strtoupper(
30
                        md5($v)
31
                    ) : md5($v, true);
32
                }
33
            }
34
        });
35
    }
36
37
    /**
38
     * Destroy the models for the given IDs.
39
     *
40
     * @param array|int $ids
41
     *
42
     * @return int
43
     */
44
    public static function destroy($ids)
45
    {
46
        // We'll initialize a count here so we will return the total number of deletes
47
        // for the operation. The developers can then check this number as a boolean
48
        // type value or get this total count of records deleted for logging, etc.
49
        $count = 0;
50
51
        $ids = is_array($ids) ? $ids : func_get_args();
52
53
        foreach ((new static())->applyIds($ids)->get() as $model) {
0 ignored issues
show
It seems like applyIds() 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

53
        foreach ((new static())->/** @scrutinizer ignore-call */ applyIds($ids)->get() as $model) {
Loading history...
54
            if ($model->delete()) {
55
                $count++;
56
            }
57
        }
58
59
        return $count;
60
    }
61
62
    /**
63
     * Convert the object into something JSON serializable.
64
     *
65
     * @return array
66
     */
67
    public function jsonSerialize()
68
    {
69
        $attributes = $this->toArray();
0 ignored issues
show
It seems like toArray() 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

69
        /** @scrutinizer ignore-call */ 
70
        $attributes = $this->toArray();
Loading history...
70
        foreach ($attributes as $key => $value) {
71
            if (in_array($key, $this->getBinaryColumns())) {
72
                $attributes[$key] = strtoupper(bin2hex($value));
73
            }
74
        }
75
76
        // append virtual row id
77
        if (count($this->getRawKeyName()) > 1) {
78
            $attributes[$this->getNormalizedKeyName()] = $this->getNormalizedKey();
79
        }
80
81
        return $attributes;
82
    }
83
84
    /**
85
     * Get the primary key for the model.
86
     *
87
     * @return array
88
     */
89
    public function getRawKeyName()
90
    {
91
        return $this->hasCompositeIndex() ? $this->primaryKey : [$this->primaryKey];
92
    }
93
94
    /**
95
     * Get the value of the model's primary key.
96
     *
97
     * @return mixed
98
     */
99
    public function getRawKey()
100
    {
101
        $attributes = [];
102
103
        foreach ($this->getRawKeyName() as $key) {
104
            $attributes[$key] = $this->getAttribute($key);
0 ignored issues
show
It seems like getAttribute() 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

104
            /** @scrutinizer ignore-call */ 
105
            $attributes[$key] = $this->getAttribute($key);
Loading history...
105
        }
106
107
        return $attributes;
108
    }
109
110
    /**
111
     * Get the value indicating whether the IDs are incrementing.
112
     *
113
     * @return bool
114
     */
115
    public function getIncrementing()
116
    {
117
        return false;
118
    }
119
120
    /**
121
     * Get the primary key for the model.
122
     *
123
     * @return string
124
     */
125
    public function getKeyName()
126
    {
127
        return $this->getNormalizedKeyName();
128
    }
129
130
    /**
131
     * Get virtual string key, required for proper collection support.
132
     *
133
     * @return mixed
134
     */
135
    public function getKey()
136
    {
137
        return $this->getNormalizedKey();
138
    }
139
140
    /**
141
     * @param \Illuminate\Database\Query\Builder $query
142
     * @param array|string                       $ids
143
     * @param bool                               $inverse
144
     *
145
     * @throws MissingPrimaryKeyValueException
146
     * @throws WrongKeyException
147
     */
148
    public function scopeApplyIds($query, $ids, $inverse = false)
149
    {
150
        $keys = ($instance = new static())->getRawKeyName();
0 ignored issues
show
The assignment to $instance is dead and can be removed.
Loading history...
151
152
        if (!is_array($ids) || Arr::isAssoc($ids)) {
153
            $ids = [$ids];
154
        }
155
156
        if ($this->hasCompositeIndex()) {
157
            (new CompositeKeyScope($keys, $ids, $inverse, $this->getBinaryColumns()))->apply($query);
158
        } else {
159
            //remap hex ID to binary ID even if index is not composite
160
            if ($this->shouldProcessBinaryAttribute($keys[0])) {
161
                $ids = array_map(function ($hex) use ($keys) {
162
                    return $this->recoverBinaryKey($keys[0], $hex);
163
                }, $ids);
164
            }
165
            if ($inverse) {
166
                $query->whereNotIn($this->qualifyColumn($keys[0]), $ids);
0 ignored issues
show
It seems like qualifyColumn() 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

166
                $query->whereNotIn($this->/** @scrutinizer ignore-call */ qualifyColumn($keys[0]), $ids);
Loading history...
167
            } else {
168
                $query->whereIn($this->qualifyColumn($keys[0]), $ids);
169
            }
170
        }
171
    }
172
173
    /**
174
     * Get a new query to restore one or more models by their queueable IDs.
175
     *
176
     * @param array|int $ids
177
     *
178
     *@throws WrongKeyException
179
     * @throws MissingPrimaryKeyValueException
180
     *
181
     * @return Builder
182
     */
183
    public function newQueryForRestoration($ids)
184
    {
185
        if (!is_array($ids)) {
186
            $ids = [$ids];
187
        }
188
189
        return $this->newQueryWithoutScopes()->applyIds(
0 ignored issues
show
It seems like newQueryWithoutScopes() 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

189
        return $this->/** @scrutinizer ignore-call */ newQueryWithoutScopes()->applyIds(
Loading history...
190
            array_map(
191
                function ($normalizedKey) {
192
                    return $this->hasCompositeIndex() ? $this->parseNormalizedKey($normalizedKey) : $normalizedKey;
193
                },
194
                $ids
195
            )
196
        );
197
    }
198
199
    /**
200
     * @param \Illuminate\Database\Query\Builder $query
201
     *
202
     * @return Builder|static
203
     */
204
    public function newEloquentBuilder($query)
205
    {
206
        return new CompositeKeyQueryBuilder($query);
207
    }
208
209
    public function getBinaryColumns()
210
    {
211
        return $this->binaryColumns ?? [];
212
    }
213
214
    /**
215
     * Get the primary key value for a save query.
216
     *
217
     * @return mixed
218
     */
219
    protected function getKeyForSaveQuery()
220
    {
221
        $originalKeys = array_intersect_key($this->original, array_flip($this->getRawKeyName()));
222
223
        return array_merge($this->getRawKey(), $originalKeys);
224
    }
225
226
    /**
227
     * Set the keys for a save update query.
228
     *
229
     * @param Builder $query
230
     *
231
     *@throws MissingPrimaryKeyValueException
232
     *
233
     * @return Builder
234
     */
235
    protected function setKeysForSaveQuery(Builder $query)
236
    {
237
        foreach ($this->getRawKeyName() as $key) {
238
            if (isset($this->{$key})) {
239
                $query->where($key, '=', $this->getAttributeFromArray($key));
0 ignored issues
show
It seems like getAttributeFromArray() 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

239
                $query->where($key, '=', $this->/** @scrutinizer ignore-call */ getAttributeFromArray($key));
Loading history...
240
            } else {
241
                throw new MissingPrimaryKeyValueException($key, 'Missing value for key '.$key);
242
            }
243
        }
244
245
        return $query;
246
    }
247
248
    /**
249
     * Run the increment or decrement method on the model.
250
     *
251
     * @param string    $column
252
     * @param float|int $amount
253
     * @param array     $extra
254
     * @param string    $method
255
     *
256
     * @return int
257
     */
258
    protected function incrementOrDecrement($column, $amount, $extra, $method)
259
    {
260
        $query = $this->newQuery();
0 ignored issues
show
The method newQuery() does not exist on MaksimM\CompositePrimary...\HasCompositePrimaryKey. Did you maybe mean newQueryForRestoration()? ( Ignorable by Annotation )

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

260
        /** @scrutinizer ignore-call */ 
261
        $query = $this->newQuery();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
261
262
        if (!$this->exists) {
263
            return $query->{$method}($column, $amount, $extra);
264
        }
265
266
        $this->incrementOrDecrementAttributeValue($column, $amount, $extra, $method);
0 ignored issues
show
The method incrementOrDecrementAttributeValue() does not exist on MaksimM\CompositePrimary...\HasCompositePrimaryKey. Did you maybe mean incrementOrDecrement()? ( Ignorable by Annotation )

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

266
        $this->/** @scrutinizer ignore-call */ 
267
               incrementOrDecrementAttributeValue($column, $amount, $extra, $method);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
267
268
        foreach ($this->getRawKeyName() as $key) {
269
            $query->where($key, $this->getOriginal($key));
0 ignored issues
show
It seems like getOriginal() 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

269
            $query->where($key, $this->/** @scrutinizer ignore-call */ getOriginal($key));
Loading history...
270
        }
271
272
        return $query->{$method}($column, $amount, $extra);
273
    }
274
275
    /**
276
     * Retrieve the model for a bound value.
277
     *
278
     * @param mixed $value
279
     *
280
     * @return Model|null
281
     */
282
    public function resolveRouteBinding($value)
283
    {
284
        if ($this->hasCompositeIndex() && $this->getRouteKeyName() == $this->getKeyName()) {
0 ignored issues
show
It seems like getRouteKeyName() 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

284
        if ($this->hasCompositeIndex() && $this->/** @scrutinizer ignore-call */ getRouteKeyName() == $this->getKeyName()) {
Loading history...
285
            return $this->whereKey($value)->first();
0 ignored issues
show
It seems like whereKey() 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

285
            return $this->/** @scrutinizer ignore-call */ whereKey($value)->first();
Loading history...
286
        } else {
287
            return $this->where($this->getRouteKeyName(), $value)->first();
0 ignored issues
show
It seems like where() 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

287
            return $this->/** @scrutinizer ignore-call */ where($this->getRouteKeyName(), $value)->first();
Loading history...
288
        }
289
    }
290
}
291