Completed
Push — master ( c48868...d3684a )
by Maksim
20s queued 11s
created

HasCompositePrimaryKey   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 40
eloc 72
c 2
b 1
f 0
dl 0
loc 272
rs 9.2

16 Methods

Rating   Name   Duplication   Size   Complexity  
A newQueryForRestoration() 0 12 2
A getKeyName() 0 3 1
A getKey() 0 3 1
A getKeyForSaveQuery() 0 5 1
A bootHasCompositePrimaryKey() 0 9 5
A setKeysForSaveQuery() 0 11 3
A incrementOrDecrement() 0 15 3
A scopeApplyIds() 0 21 6
A getRawKey() 0 9 2
A getIncrementing() 0 3 1
A getBinaryColumns() 0 3 1
A destroy() 0 16 4
A jsonSerialize() 0 15 4
A newEloquentBuilder() 0 3 1
A resolveRouteBinding() 0 6 3
A getRawKeyName() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like HasCompositePrimaryKey often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HasCompositePrimaryKey, and based on these observations, apply Extract Interface, too.

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, OptionalBinaryTransformation;
16
17
    /**
18
     * Automatically generate unique binary id.
19
     */
20
    public static function bootHasCompositePrimaryKey()
21
    {
22
        static::creating(function ($model) {
23
            foreach ($model->getRawKeyName() as $key) {
24
                if (!isset($model->{$key}) && in_array($key, $model->getBinaryColumns())) {
25
                    $v = uniqid(rand(), true);
26
                    $model->{$key} = $model->hexBinaryColumns() ? strtoupper(
27
                        md5($v)
28
                    ) : md5($v, true);
29
                }
30
            }
31
        });
32
    }
33
34
    /**
35
     * Destroy the models for the given IDs.
36
     *
37
     * @param array|int $ids
38
     *
39
     * @return int
40
     */
41
    public static function destroy($ids)
42
    {
43
        // We'll initialize a count here so we will return the total number of deletes
44
        // for the operation. The developers can then check this number as a boolean
45
        // type value or get this total count of records deleted for logging, etc.
46
        $count = 0;
47
48
        $ids = is_array($ids) ? $ids : func_get_args();
49
50
        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

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

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

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

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

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

236
                $query->where($key, '=', $this->/** @scrutinizer ignore-call */ getAttributeFromArray($key));
Loading history...
237
            } else {
238
                throw new MissingPrimaryKeyValueException($key, 'Missing value for key '.$key);
239
            }
240
        }
241
242
        return $query;
243
    }
244
245
    /**
246
     * Run the increment or decrement method on the model.
247
     *
248
     * @param string    $column
249
     * @param float|int $amount
250
     * @param array     $extra
251
     * @param string    $method
252
     *
253
     * @return int
254
     */
255
    protected function incrementOrDecrement($column, $amount, $extra, $method)
256
    {
257
        $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

257
        /** @scrutinizer ignore-call */ 
258
        $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...
258
259
        if (!$this->exists) {
260
            return $query->{$method}($column, $amount, $extra);
261
        }
262
263
        $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

263
        $this->/** @scrutinizer ignore-call */ 
264
               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...
264
265
        foreach ($this->getRawKeyName() as $key) {
266
            $query->where($key, $this->getAttribute($key));
267
        }
268
269
        return $query->{$method}($column, $amount, $extra);
270
    }
271
272
    /**
273
     * Retrieve the model for a bound value.
274
     *
275
     * @param mixed $value
276
     *
277
     * @return Model|null
278
     */
279
    public function resolveRouteBinding($value)
280
    {
281
        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

281
        if ($this->hasCompositeIndex() && $this->/** @scrutinizer ignore-call */ getRouteKeyName() == $this->getKeyName()) {
Loading history...
282
            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

282
            return $this->/** @scrutinizer ignore-call */ whereKey($value)->first();
Loading history...
283
        } else {
284
            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

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