Passed
Push — master ( a1c705...b442a6 )
by Mike
07:37
created

CacheKey::getTableSlug()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
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
8
class CacheKey
9
{
10
    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...
11
12
    protected $eagerLoad;
13
    protected $model;
14
    protected $query;
15
    protected $currentBinding = 0;
16
17
    public function __construct(
18
        array $eagerLoad,
19
        Model $model,
20
        Builder $query
21
    ) {
22
        $this->eagerLoad = $eagerLoad;
23
        $this->model = $model;
24
        $this->query = $query;
25
    }
26
27
    public function make(
28
        array $columns = ["*"],
29
        $idColumn = null,
30
        string $keyDifferentiator = ""
31
    ) : string {
32
        $key = $this->getCachePrefix();
33
        $key .= $this->getTableSlug();
34
        $key .= $this->getModelSlug();
35
        $key .= $this->getIdColumn($idColumn ?: "");
36
        $key .= $this->getQueryColumns($columns);
37
        $key .= $this->getWhereClauses();
38
        $key .= $this->getWithModels();
39
        $key .= $this->getOrderByClauses();
40
        $key .= $this->getOffsetClause();
41
        $key .= $this->getLimitClause();
42
        $key .= $keyDifferentiator;
43
44
        return $key;
45
    }
46
47
    protected function getIdColumn(string $idColumn) : string
48
    {
49
        return $idColumn ? "_{$idColumn}" : "";
50
    }
51
52
    protected function getLimitClause() : string
53
    {
54
        if (! $this->query->limit) {
55
            return "";
56
        }
57
58
        return "-limit_{$this->query->limit}";
59
    }
60
61
    protected function getTableSlug() : string
62
    {
63
        return str_slug($this->model->getTable())
64
            . ":";
65
    }
66
67
    protected function getModelSlug() : string
68
    {
69
        return str_slug(get_class($this->model));
70
    }
71
72
    protected function getOffsetClause() : string
73
    {
74
        if (! $this->query->offset) {
75
            return "";
76
        }
77
78
        return "-offset_{$this->query->offset}";
79
    }
80
81
    protected function getOrderByClauses() : string
82
    {
83
        $orders = collect($this->query->orders);
84
85
        return $orders
86
            ->reduce(function ($carry, $order) {
87
                if (($order["type"] ?? "") === "Raw") {
88
                    return $carry . "_orderByRaw_" . str_slug($order["sql"]);
89
                }
90
91
                return $carry . "_orderBy_" . $order["column"] . "_" . $order["direction"];
92
            })
93
            ?: "";
94
    }
95
96
    protected function getQueryColumns(array $columns) : string
97
    {
98
        if ($columns === ["*"] || $columns === []) {
99
            return "";
100
        }
101
102
        return "_" . implode("_", $columns);
103
    }
104
105
    protected function getTypeClause($where) : string
106
    {
107
        $type = in_array($where["type"], ["InRaw", "In", "NotIn", "Null", "NotNull", "between", "NotInSub", "InSub", "JsonContains"])
108
            ? strtolower($where["type"])
109
            : strtolower($where["operator"]);
110
111
        return str_replace(" ", "_", $type);
112
    }
113
114
    protected function getValuesClause(array $where = null) : string
115
    {
116
        if (in_array($where["type"], ["NotNull", "Null"])) {
117
            return "";
118
        }
119
120
        $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

120
        $values = $this->getValuesFromWhere(/** @scrutinizer ignore-type */ $where);
Loading history...
121
        $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

121
        $values = $this->getValuesFromBindings(/** @scrutinizer ignore-type */ $where, $values);
Loading history...
122
123
124
125
        return "_" . $values;
126
    }
127
128
    protected function getValuesFromWhere(array $where) : string
129
    {
130
        if (array_get($where, "query")) {
131
            $prefix = $this->getCachePrefix();
132
            $subKey = (new self($this->eagerLoad, $this->model, $where["query"]))
133
                ->make();
134
            $subKey = str_replace($prefix, "", $subKey);
135
            $subKey = str_replace($this->getModelSlug(), "", $subKey);
136
            $classParts = explode("\\", get_class($this->model));
137
            $subKey = strtolower(array_pop($classParts)) . $subKey;
138
139
            return $subKey;
140
        }
141
142
        if (is_array(array_get($where, "values"))) {
143
            return implode("_", $where["values"]);
144
        }
145
146
        return array_get($where, "value", "");
147
    }
148
149
    protected function getValuesFromBindings(array $where, string $values) : string
150
    {
151
        if (! $values && ($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->getInClauses($where);
173
                $value .= $this->getNotInClauses($where);
174
                $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

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