Passed
Pull Request — master (#8)
by Maksim
02:53
created

HasCompositePrimaryKey::destroy()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 16
rs 10
cc 4
nc 6
nop 1
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, PrimaryKeyInformation, CompositeRelationships;
16
17
    protected $hexBinaryColumns = false;
18
19
    /**
20
     * Automatically generate unique binary id.
21
     */
22
    public static function bootHasCompositePrimaryKey()
23
    {
24
        static::creating(function ($model) {
25
            foreach($model->getRawKeyName() as $key) {
26
                if(!isset($model->{$key}) && in_array($key, $model->getBinaryColumns())) {
27
                    $v = uniqid(rand(), true);
28
                    $model->{$key} = $model->hexBinaryColumns() ? strtoupper(
29
                        md5($v)
30
                    ) : md5($v, true);
31
                }
32
            }
33
        });
34
    }
35
36
    /**
37
     * Destroy the models for the given IDs.
38
     *
39
     * @param array|int $ids
40
     *
41
     * @return int
42
     */
43
    public static function destroy($ids)
44
    {
45
        // We'll initialize a count here so we will return the total number of deletes
46
        // for the operation. The developers can then check this number as a boolean
47
        // type value or get this total count of records deleted for logging, etc.
48
        $count = 0;
49
50
        $ids = is_array($ids) ? $ids : func_get_args();
51
52
        foreach ((new static())->applyIds($ids)->get() as $model) {
0 ignored issues
show
Bug introduced by
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

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

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

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

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

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

243
                $query->where($key, '=', $this->/** @scrutinizer ignore-call */ getAttributeFromArray($key));
Loading history...
244
            } else {
245
                throw new MissingPrimaryKeyValueException($key, 'Missing value for key '.$key);
246
            }
247
        }
248
249
        return $query;
250
    }
251
252
    /**
253
     * Run the increment or decrement method on the model.
254
     *
255
     * @param string    $column
256
     * @param float|int $amount
257
     * @param array     $extra
258
     * @param string    $method
259
     *
260
     * @return int
261
     */
262
    protected function incrementOrDecrement($column, $amount, $extra, $method)
263
    {
264
        $query = $this->newQuery();
0 ignored issues
show
Bug introduced by
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

264
        /** @scrutinizer ignore-call */ 
265
        $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...
265
266
        if (!$this->exists) {
267
            return $query->{$method}($column, $amount, $extra);
268
        }
269
270
        $this->incrementOrDecrementAttributeValue($column, $amount, $extra, $method);
0 ignored issues
show
Bug introduced by
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

270
        $this->/** @scrutinizer ignore-call */ 
271
               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...
271
272
        foreach ($this->getRawKeyName() as $key) {
273
            $query->where($key, $this->getAttribute($key));
274
        }
275
276
        return $query->{$method}($column, $amount, $extra);
277
    }
278
279
    /**
280
     * Retrieve the model for a bound value.
281
     *
282
     * @param mixed $value
283
     *
284
     * @return Model|null
285
     */
286
    public function resolveRouteBinding($value)
287
    {
288
        if ($this->hasCompositeIndex() && $this->getRouteKeyName() == $this->getKeyName()) {
0 ignored issues
show
Bug introduced by
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

288
        if ($this->hasCompositeIndex() && $this->/** @scrutinizer ignore-call */ getRouteKeyName() == $this->getKeyName()) {
Loading history...
289
            return $this->whereKey($value)->first();
0 ignored issues
show
Bug introduced by
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

289
            return $this->/** @scrutinizer ignore-call */ whereKey($value)->first();
Loading history...
290
        } else {
291
            return $this->where($this->getRouteKeyName(), $value)->first();
0 ignored issues
show
Bug introduced by
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

291
            return $this->/** @scrutinizer ignore-call */ where($this->getRouteKeyName(), $value)->first();
Loading history...
292
        }
293
    }
294
295
    private function shouldProcessBinaryAttribute($key)
296
    {
297
        return $this->hexBinaryColumns() && in_array($key, $this->getBinaryColumns());
298
    }
299
300
    public function hasGetMutator($key)
301
    {
302
        if ($this->shouldProcessBinaryAttribute($key) && isset($this->{$key})) {
303
            return true;
304
        }
305
306
        return parent::hasGetMutator($key);
307
    }
308
309
    public function mutateAttribute($key, $value)
310
    {
311
        if ($this->shouldProcessBinaryAttribute($key)) {
312
            return strtoupper(bin2hex($value));
313
        }
314
315
        return parent::mutateAttribute($key, $value);
316
    }
317
318
    public function hasSetMutator($key)
319
    {
320
        if ($this->shouldProcessBinaryAttribute($key)) {
321
            return true;
322
        }
323
324
        return parent::hasSetMutator($key);
325
    }
326
327
    public function setMutatedAttributeValue($key, $value)
328
    {
329
        if ($this->shouldProcessBinaryAttribute($key)) {
330
            $value = hex2bin($value);
331
            $this->attributes[$key] = $value;
0 ignored issues
show
Bug Best Practice introduced by
The property attributes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
332
333
            return $value;
334
        }
335
336
        return parent::setMutatedAttributeValue($key, $value);
337
    }
338
}
339