Passed
Push — master ( 6997ac...30bfa2 )
by Mike
02:50
created

CacheKey   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 137
dl 0
loc 302
rs 6.96
c 0
b 0
f 0
wmc 53

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getWhereClauses() 0 12 1
A getOtherClauses() 0 10 2
A getLimitClause() 0 7 2
A getModelSlug() 0 3 1
A getInAndNotInClauses() 0 14 2
A recursiveImplode() 0 28 6
A __construct() 0 8 1
A getIdColumn() 0 3 2
A make() 0 18 2
A getValuesFromBindings() 0 14 4
A getOrderByClauses() 0 13 3
A getOffsetClause() 0 7 2
A getNestedClauses() 0 7 2
A getTypeClause() 0 7 2
A getWheres() 0 9 2
A getRawClauses() 0 22 4
A getColumnClauses() 0 7 2
A getWithModels() 0 9 2
A getValuesFromWhere() 0 19 3
A getQueryColumns() 0 7 3
A getInClauses() 0 10 2
A getTableSlug() 0 4 1
A getValuesClause() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like CacheKey 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 CacheKey, and based on these observations, apply Extract Interface, too.

1
<?php namespace GeneaLabs\LaravelModelCaching;
2
3
use GeneaLabs\LaravelModelCaching\Traits\CachePrefixing;
4
use Illuminate\Database\Eloquent\Model;
5
use Illuminate\Database\Query\Builder;
6
use Illuminate\Support\Collection;
7
use Illuminate\Database\Query\Expression;
8
9
class CacheKey
10
{
11
    use CachePrefixing;
1 ignored issue
show
Bug introduced by
The trait GeneaLabs\LaravelModelCa...g\Traits\CachePrefixing requires the property $connection which is not provided by GeneaLabs\LaravelModelCaching\CacheKey.
Loading history...
12
13
    protected $eagerLoad;
14
    protected $model;
15
    protected $query;
16
    protected $currentBinding = 0;
17
18
    public function __construct(
19
        array $eagerLoad,
20
        Model $model,
21
        Builder $query
22
    ) {
23
        $this->eagerLoad = $eagerLoad;
24
        $this->model = $model;
25
        $this->query = $query;
26
    }
27
28
    public function make(
29
        array $columns = ["*"],
30
        $idColumn = null,
31
        string $keyDifferentiator = ""
32
    ) : string {
33
        $key = $this->getCachePrefix();
34
        $key .= $this->getTableSlug();
35
        $key .= $this->getModelSlug();
36
        $key .= $this->getIdColumn($idColumn ?: "");
37
        $key .= $this->getQueryColumns($columns);
38
        $key .= $this->getWhereClauses();
39
        $key .= $this->getWithModels();
40
        $key .= $this->getOrderByClauses();
41
        $key .= $this->getOffsetClause();
42
        $key .= $this->getLimitClause();
43
        $key .= $keyDifferentiator;
44
// dump($key);
45
        return $key;
46
    }
47
48
    protected function getIdColumn(string $idColumn) : string
49
    {
50
        return $idColumn ? "_{$idColumn}" : "";
51
    }
52
53
    protected function getLimitClause() : string
54
    {
55
        if (! $this->query->limit) {
56
            return "";
57
        }
58
59
        return "-limit_{$this->query->limit}";
60
    }
61
62
    protected function getTableSlug() : string
63
    {
64
        return str_slug($this->model->getTable())
0 ignored issues
show
Deprecated Code introduced by
The function str_slug() has been deprecated: Str::slug() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

64
        return /** @scrutinizer ignore-deprecated */ str_slug($this->model->getTable())

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
65
            . ":";
66
    }
67
68
    protected function getModelSlug() : string
69
    {
70
        return str_slug(get_class($this->model));
0 ignored issues
show
Deprecated Code introduced by
The function str_slug() has been deprecated: Str::slug() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

70
        return /** @scrutinizer ignore-deprecated */ str_slug(get_class($this->model));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
71
    }
72
73
    protected function getOffsetClause() : string
74
    {
75
        if (! $this->query->offset) {
76
            return "";
77
        }
78
79
        return "-offset_{$this->query->offset}";
80
    }
81
82
    protected function getOrderByClauses() : string
83
    {
84
        $orders = collect($this->query->orders);
85
86
        return $orders
87
            ->reduce(function ($carry, $order) {
88
                if (($order["type"] ?? "") === "Raw") {
89
                    return $carry . "_orderByRaw_" . str_slug($order["sql"]);
0 ignored issues
show
Deprecated Code introduced by
The function str_slug() has been deprecated: Str::slug() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

89
                    return $carry . "_orderByRaw_" . /** @scrutinizer ignore-deprecated */ str_slug($order["sql"]);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
90
                }
91
92
                return $carry . "_orderBy_" . $order["column"] . "_" . $order["direction"];
93
            })
94
            ?: "";
95
    }
96
97
    protected function getQueryColumns(array $columns) : string
98
    {
99
        if ($columns === ["*"] || $columns === []) {
100
            return "";
101
        }
102
103
        return "_" . implode("_", $columns);
104
    }
105
106
    protected function getTypeClause($where) : string
107
    {
108
        $type = in_array($where["type"], ["InRaw", "In", "NotIn", "Null", "NotNull", "between", "NotInSub", "InSub", "JsonContains"])
109
            ? strtolower($where["type"])
110
            : strtolower($where["operator"]);
111
112
        return str_replace(" ", "_", $type);
113
    }
114
115
    protected function getValuesClause(array $where = null) : string
116
    {
117
        if (in_array($where["type"], ["NotNull", "Null"])) {
118
            return "";
119
        }
120
121
        $values = $this->getValuesFromWhere($where);
0 ignored issues
show
Bug introduced by
It seems like $where can also be of type null; however, parameter $where of GeneaLabs\LaravelModelCa...y::getValuesFromWhere() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

121
        $values = $this->getValuesFromWhere(/** @scrutinizer ignore-type */ $where);
Loading history...
122
        $values = $this->getValuesFromBindings($where, $values);
0 ignored issues
show
Bug introduced by
It seems like $where can also be of type null; however, parameter $where of GeneaLabs\LaravelModelCa...getValuesFromBindings() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

122
        $values = $this->getValuesFromBindings(/** @scrutinizer ignore-type */ $where, $values);
Loading history...
123
124
        return "_" . $values;
125
    }
126
127
    protected function getValuesFromWhere(array $where) : string
128
    {
129
        if (array_get($where, "query")) {
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

129
        if (/** @scrutinizer ignore-deprecated */ array_get($where, "query")) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
130
            $prefix = $this->getCachePrefix();
131
            $subKey = (new self($this->eagerLoad, $this->model, $where["query"]))
132
                ->make();
133
            $subKey = str_replace($prefix, "", $subKey);
134
            $subKey = str_replace($this->getModelSlug(), "", $subKey);
135
            $classParts = explode("\\", get_class($this->model));
136
            $subKey = strtolower(array_pop($classParts)) . $subKey;
137
138
            return $subKey;
139
        }
140
141
        if (is_array(array_get($where, "values"))) {
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

141
        if (is_array(/** @scrutinizer ignore-deprecated */ array_get($where, "values"))) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
142
            return implode("_", collect($where["values"])->flatten()->toArray());
143
        }
144
145
        return array_get($where, "value", "");
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated: Arr::get() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

145
        return /** @scrutinizer ignore-deprecated */ array_get($where, "value", "");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
146
    }
147
148
    protected function getValuesFromBindings(array $where, string $values) : string
149
    {
150
        // if (! $values && ($this->query->bindings["where"][$this->currentBinding] ?? false)) {
151
        if ($this->query->bindings["where"][$this->currentBinding] ?? false) {
152
            $values = $this->query->bindings["where"][$this->currentBinding];
153
            $this->currentBinding++;
154
155
            if ($where["type"] === "between") {
156
                $values .= "_" . $this->query->bindings["where"][$this->currentBinding];
157
                $this->currentBinding++;
158
            }
159
        }
160
161
        return $values ?: "";
162
    }
163
164
    protected function getWhereClauses(array $wheres = []) : string
165
    {
166
        return "" . $this->getWheres($wheres)
167
            ->reduce(function ($carry, $where) {
168
                $value = $carry;
169
                $value .= $this->getNestedClauses($where);
170
                $value .= $this->getColumnClauses($where);
171
                $value .= $this->getRawClauses($where);
172
                $value .= $this->getInAndNotInClauses($where);
173
                $value .= $this->getOtherClauses($where, $carry);
0 ignored issues
show
Unused Code introduced by
The call to GeneaLabs\LaravelModelCa...eKey::getOtherClauses() has too many arguments starting with $carry. ( Ignorable by Annotation )

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

173
                $value .= $this->/** @scrutinizer ignore-call */ getOtherClauses($where, $carry);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
174
175
                return $value;
176
            });
177
    }
178
179
    protected function getNestedClauses(array $where) : string
180
    {
181
        if (! in_array($where["type"], ["Exists", "Nested", "NotExists"])) {
182
            return "";
183
        }
184
185
        return "-" . strtolower($where["type"]) . $this->getWhereClauses($where["query"]->wheres);
186
    }
187
188
    protected function getColumnClauses(array $where) : string
189
    {
190
        if ($where["type"] !== "Column") {
191
            return "";
192
        }
193
194
        return "-{$where["boolean"]}_{$where["first"]}_{$where["operator"]}_{$where["second"]}";
195
    }
196
197
    protected function getInClauses(array $where) : string
198
    {
199
        if (! in_array($where["type"], ["In"])) {
200
            return "";
201
        }
202
203
        $this->currentBinding++;
204
        $values = $this->recursiveImplode($where["values"], "_");
205
206
        return "-{$where["column"]}_in{$values}";
207
    }
208
209
    protected function getInAndNotInClauses(array $where) : string
210
    {
211
        if (! in_array($where["type"], ["In", "NotIn"])) {
212
            return "";
213
        }
214
215
        $type = strtolower($where["type"]);
216
        $subquery = $this->getValuesFromWhere($where);
217
        $values = collect($this->query->bindings["where"][$this->currentBinding]);
218
        $this->currentBinding++;
219
        $subquery = collect(vsprintf(str_replace("?", "%s", $subquery), $values->toArray()));
220
        $values = $this->recursiveImplode($subquery->toArray(), "_");
221
222
        return "-{$where["column"]}_{$type}{$values}";
223
    }
224
225
    protected function recursiveImplode(array $items, string $glue = ",") : string
226
    {
227
        $result = "";
228
229
        foreach ($items as $value) {
230
            if ($value instanceof Expression) {
231
                $value = $value->getValue();
232
            }
233
234
            if (is_string($value)) {
235
                $value = str_replace('"', '', $value);
236
                $value = explode(" ", $value);
237
238
                if (count($value) === 1) {
239
                    $value = $value[0];
240
                }
241
            }
242
243
            if (is_array($value)) {
244
                $result .= $this->recursiveImplode($value, $glue);
245
246
                continue;
247
            }
248
249
            $result .= $glue . $value;
250
        }
251
252
        return $result;
253
    }
254
255
    protected function getRawClauses(array $where) : string
256
    {
257
        if ($where["type"] !== "raw") {
258
            return "";
259
        }
260
261
        $queryParts = explode("?", $where["sql"]);
262
        $clause = "_{$where["boolean"]}";
263
264
        while (count($queryParts) > 1) {
265
            $clause .= "_" . array_shift($queryParts);
266
            $clause .= $this->query->bindings["where"][$this->currentBinding];
267
            $this->currentBinding++;
268
        }
269
270
        $lastPart = array_shift($queryParts);
271
272
        if ($lastPart) {
273
            $clause .= "_" . $lastPart;
274
        }
275
276
        return "-" . str_replace(" ", "_", $clause);
277
    }
278
279
    protected function getOtherClauses(array $where) : string
280
    {
281
        if (in_array($where["type"], ["Exists", "Nested", "NotExists", "Column", "raw", "In", "NotIn"])) {
282
            return "";
283
        }
284
285
        $value = $this->getTypeClause($where);
286
        $value .= $this->getValuesClause($where);
287
288
        return "-{$where["column"]}_{$value}";
289
    }
290
291
    protected function getWheres(array $wheres) : Collection
292
    {
293
        $wheres = collect($wheres);
294
295
        if ($wheres->isEmpty()) {
296
            $wheres = collect($this->query->wheres);
297
        }
298
299
        return $wheres;
300
    }
301
302
    protected function getWithModels() : string
303
    {
304
        $eagerLoads = collect($this->eagerLoad);
305
306
        if ($eagerLoads->isEmpty()) {
307
            return "";
308
        }
309
310
        return "-" . implode("-", $eagerLoads->keys()->toArray());
311
    }
312
}
313