Passed
Push — master ( 801dac...26623e )
by Mike
03:27 queued 10s
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\Support\Arr;
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Str;
7
8
class CacheKey
9
{
10
    use CachePrefixing;
11
12
    protected $eagerLoad;
13
    protected $model;
14
    protected $query;
15
    protected $currentBinding = 0;
16
17
    public function __construct(
18
        array $eagerLoad,
19
        $model,
20
        $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 (! property_exists($this->query, "limit")
55
            || ! $this->query->limit
56
        ) {
57
            return "";
58
        }
59
60
        return "-limit_{$this->query->limit}";
61
    }
62
63
    protected function getTableSlug() : string
64
    {
65
        return (new Str)->slug($this->model->getTable())
66
            . ":";
67
    }
68
69
    protected function getModelSlug() : string
70
    {
71
        return (new Str)->slug(get_class($this->model));
72
    }
73
74
    protected function getOffsetClause() : string
75
    {
76
        if (! property_exists($this->query, "offset")
77
            || ! $this->query->offset
78
        ) {
79
            return "";
80
        }
81
82
        return "-offset_{$this->query->offset}";
83
    }
84
85
    protected function getOrderByClauses() : string
86
    {
87
        if (! property_exists($this->query, "orders")
88
            || ! $this->query->orders
89
        ) {
90
            return "";
91
        }
92
93
        $orders = collect($this->query->orders);
94
95
        return $orders
96
            ->reduce(function ($carry, $order) {
97
                if (($order["type"] ?? "") === "Raw") {
98
                    return $carry . "_orderByRaw_" . (new Str)->slug($order["sql"]);
99
                }
100
101
                return $carry . "_orderBy_" . $order["column"] . "_" . $order["direction"];
102
            })
103
            ?: "";
104
    }
105
106
    protected function getQueryColumns(array $columns) : string
107
    {
108
        if (($columns === ["*"]
109
                || $columns === [])
110
            && (! property_exists($this->query, "columns")
111
                || ! $this->query->columns)
112
        ) {
113
            return "";
114
        }
115
116
        if (property_exists($this->query, "columns")
117
            && $this->query->columns
118
        ) {
119
            return "_" . implode("_", $this->query->columns);
120
        }
121
122
        return "_" . implode("_", $columns);
123
    }
124
125
    protected function getTypeClause($where) : string
126
    {
127
        $type = in_array($where["type"], ["InRaw", "In", "NotIn", "Null", "NotNull", "between", "NotInSub", "InSub", "JsonContains"])
128
            ? strtolower($where["type"])
129
            : strtolower($where["operator"]);
130
131
        return str_replace(" ", "_", $type);
132
    }
133
134
    protected function getValuesClause(array $where = []) : string
135
    {
136
        if (! $where
137
            || in_array($where["type"], ["NotNull", "Null"])
138
        ) {
139
            return "";
140
        }
141
142
        $values = $this->getValuesFromWhere($where);
143
        $values = $this->getValuesFromBindings($where, $values);
144
145
        return "_" . $values;
146
    }
147
148
    protected function getValuesFromWhere(array $where) : string
149
    {
150
        if (array_key_exists("value", $where)
151
            && is_object($where["value"])
152
            && get_class($where["value"]) === "DateTime"
153
        ) {
154
            return $where["value"]->format("Y-m-d-H-i-s");
155
        }
156
157
        if (is_array((new Arr)->get($where, "values"))) {
158
            return implode("_", collect($where["values"])->flatten()->toArray());
159
        }
160
161
        return (new Arr)->get($where, "value", "");
162
    }
163
164
    protected function getValuesFromBindings(array $where, string $values) : string
165
    {
166
        if (($this->query->bindings["where"][$this->currentBinding] ?? false) !== false) {
167
            $values = $this->query->bindings["where"][$this->currentBinding];
168
            $this->currentBinding++;
169
170
            if ($where["type"] === "between") {
171
                $values .= "_" . $this->query->bindings["where"][$this->currentBinding];
172
                $this->currentBinding++;
173
            }
174
        }
175
176
        if (is_object($values)
177
            && get_class($values) === "DateTime"
178
        ) {
179
            $values = $values->format("Y-m-d-H-i-s");
180
        }
181
182
        return $values;
183
    }
184
185
    protected function getWhereClauses(array $wheres = []) : string
186
    {
187
        return "" . $this->getWheres($wheres)
188
            ->reduce(function ($carry, $where) {
189
                $value = $carry;
190
                $value .= $this->getNestedClauses($where);
191
                $value .= $this->getColumnClauses($where);
192
                $value .= $this->getRawClauses($where);
193
                $value .= $this->getInAndNotInClauses($where);
194
                $value .= $this->getOtherClauses($where);
195
196
                return $value;
197
            });
198
    }
199
200
    protected function getNestedClauses(array $where) : string
201
    {
202
        if (! in_array($where["type"], ["Exists", "Nested", "NotExists"])) {
203
            return "";
204
        }
205
206
        return "-" . strtolower($where["type"]) . $this->getWhereClauses($where["query"]->wheres);
207
    }
208
209
    protected function getColumnClauses(array $where) : string
210
    {
211
        if ($where["type"] !== "Column") {
212
            return "";
213
        }
214
215
        return "-{$where["boolean"]}_{$where["first"]}_{$where["operator"]}_{$where["second"]}";
216
    }
217
218
    protected function getInAndNotInClauses(array $where) : string
219
    {
220
        if (! in_array($where["type"], ["In", "NotIn", "InRaw"])) {
221
            return "";
222
        }
223
224
        $type = strtolower($where["type"]);
225
        $subquery = $this->getValuesFromWhere($where);
226
        $values = collect($this->query->bindings["where"][$this->currentBinding] ?? []);
227
        $this->currentBinding += count($where["values"]);
228
        $subquery = collect(vsprintf(str_replace("?", "%s", $subquery), $values->toArray()));
229
        $values = $this->recursiveImplode($subquery->toArray(), "_");
230
231
        return "-{$where["column"]}_{$type}{$values}";
232
    }
233
234
    protected function recursiveImplode(array $items, string $glue = ",") : string
235
    {
236
        $result = "";
237
238
        foreach ($items as $value) {
239
            if (is_string($value)) {
240
                $value = str_replace('"', '', $value);
241
                $value = explode(" ", $value);
242
243
                if (count($value) === 1) {
244
                    $value = $value[0];
245
                }
246
            }
247
248
            if (is_array($value)) {
249
                $result .= $this->recursiveImplode($value, $glue);
250
251
                continue;
252
            }
253
254
            $result .= $glue . $value;
255
        }
256
257
        return $result;
258
    }
259
260
    protected function getRawClauses(array $where) : string
261
    {
262
        if (! in_array($where["type"], ["raw"])) {
263
            return "";
264
        }
265
266
        $queryParts = explode("?", $where["sql"]);
267
        $clause = "_{$where["boolean"]}";
268
269
        while (count($queryParts) > 1) {
270
            $clause .= "_" . array_shift($queryParts);
271
            $clause .= $this->query->bindings["where"][$this->currentBinding];
272
            $this->currentBinding++;
273
        }
274
275
        $lastPart = array_shift($queryParts);
276
277
        if ($lastPart) {
278
            $clause .= "_" . $lastPart;
279
        }
280
281
        return "-" . str_replace(" ", "_", $clause);
282
    }
283
284
    protected function getOtherClauses(array $where) : string
285
    {
286
        if (in_array($where["type"], ["Exists", "Nested", "NotExists", "Column", "raw", "In", "NotIn", "InRaw"])) {
287
            return "";
288
        }
289
290
        $value = $this->getTypeClause($where);
291
        $value .= $this->getValuesClause($where);
292
293
        return "-{$where["column"]}_{$value}";
294
    }
295
296
    protected function getWheres(array $wheres) : Collection
297
    {
298
        $wheres = collect($wheres);
299
300
        if ($wheres->isEmpty()
301
            && property_exists($this->query, "wheres")
302
        ) {
303
            $wheres = collect($this->query->wheres);
304
        }
305
306
        return $wheres;
307
    }
308
309
    protected function getWithModels() : string
310
    {
311
        $eagerLoads = collect($this->eagerLoad);
312
313
        if ($eagerLoads->isEmpty()) {
314
            return "";
315
        }
316
317
        return $eagerLoads->keys()->reduce(function ($carry, $related) {
318
            if (! method_exists($this->model, $related)) {
319
                return "{$carry}-{$related}";
320
            }
321
322
            $relatedModel = $this->model->$related()->getRelated();
323
            $relatedConnection = $relatedModel->getConnection()->getName();
324
            $relatedDatabase = $relatedModel->getConnection()->getDatabaseName();
325
326
            return "{$carry}-{$relatedConnection}:{$relatedDatabase}:{$related}";
327
        });
328
    }
329
}
330