Completed
Push — master ( fec279...e7bead )
by Mike
16s queued 11s
created

CacheKey::getIdColumn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 2
eloc 1
nc 2
nop 1
1
<?php namespace GeneaLabs\LaravelModelCaching;
2
3
use Exception;
4
use GeneaLabs\LaravelModelCaching\Traits\CachePrefixing;
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Str;
8
use Ramsey\Uuid\Uuid;
9
10
class CacheKey
11
{
12
    use CachePrefixing;
13
14
    protected $currentBinding = 0;
15
    protected $eagerLoad;
16
    protected $macroKey;
17
    protected $model;
18
    protected $query;
19
20
    public function __construct(
21
        array $eagerLoad,
22
        $model,
23
        $query,
24
        $macroKey
25
    ) {
26
        $this->eagerLoad = $eagerLoad;
27
        $this->macroKey = $macroKey;
28
        $this->model = $model;
29
        $this->query = $query;
30
    }
31
32
    public function make(
33
        array $columns = ["*"],
34
        $idColumn = null,
35
        string $keyDifferentiator = ""
36
    ) : string {
37
        $key = $this->getCachePrefix();
38
        $key .= $this->getTableSlug();
39
        $key .= $this->getModelSlug();
40
        $key .= $this->getIdColumn($idColumn ?: "");
41
        $key .= $this->getQueryColumns($columns);
42
        $key .= $this->getWhereClauses();
43
        $key .= $this->getWithModels();
44
        $key .= $this->getOrderByClauses();
45
        $key .= $this->getOffsetClause();
46
        $key .= $this->getLimitClause();
47
        $key .= $this->getBindingsSlug();
48
        $key .= $keyDifferentiator;
49
        $key .= $this->macroKey;
50
// dump($key);
51
        return $key;
52
    }
53
54
    protected function getIdColumn(string $idColumn) : string
55
    {
56
        return $idColumn ? "_{$idColumn}" : "";
57
    }
58
59
    protected function getLimitClause() : string
60
    {
61
        if (! property_exists($this->query, "limit")
62
            || ! $this->query->limit
63
        ) {
64
            return "";
65
        }
66
67
        return "-limit_{$this->query->limit}";
68
    }
69
70
    protected function getTableSlug() : string
71
    {
72
        return (new Str)->slug($this->model->getTable())
73
            . ":";
74
    }
75
76
    protected function getModelSlug() : string
77
    {
78
        return (new Str)->slug(get_class($this->model));
79
    }
80
81
    protected function getOffsetClause() : string
82
    {
83
        if (! property_exists($this->query, "offset")
84
            || ! $this->query->offset
85
        ) {
86
            return "";
87
        }
88
89
        return "-offset_{$this->query->offset}";
90
    }
91
92
    protected function getOrderByClauses() : string
93
    {
94
        if (! property_exists($this->query, "orders")
95
            || ! $this->query->orders
96
        ) {
97
            return "";
98
        }
99
100
        $orders = collect($this->query->orders);
101
102
        return $orders
103
            ->reduce(function ($carry, $order) {
104
                if (($order["type"] ?? "") === "Raw") {
105
                    return $carry . "_orderByRaw_" . (new Str)->slug($order["sql"]);
106
                }
107
108
                return $carry . "_orderBy_" . $order["column"] . "_" . $order["direction"];
109
            })
110
            ?: "";
111
    }
112
113
    protected function getQueryColumns(array $columns) : string
114
    {
115
        if (($columns === ["*"]
116
                || $columns === [])
117
            && (! property_exists($this->query, "columns")
118
                || ! $this->query->columns)
119
        ) {
120
            return "";
121
        }
122
123
        if (property_exists($this->query, "columns")
124
            && $this->query->columns
125
        ) {
126
            return "_" . implode("_", $this->query->columns);
127
        }
128
129
        return "_" . implode("_", $columns);
130
    }
131
132
    protected function getTypeClause($where) : string
133
    {
134
        $type = in_array($where["type"], ["InRaw", "In", "NotIn", "Null", "NotNull", "between", "NotInSub", "InSub", "JsonContains"])
135
            ? strtolower($where["type"])
136
            : strtolower($where["operator"]);
137
138
        return str_replace(" ", "_", $type);
139
    }
140
141
    protected function getValuesClause(array $where = []) : string
142
    {
143
        if (! $where
144
            || in_array($where["type"], ["NotNull", "Null"])
145
        ) {
146
            return "";
147
        }
148
149
        $values = $this->getValuesFromWhere($where);
150
        $values = $this->getValuesFromBindings($where, $values);
151
152
        return "_" . $values;
153
    }
154
155
    protected function getValuesFromWhere(array $where) : string
156
    {
157
        if (array_key_exists("value", $where)
158
            && is_object($where["value"])
159
            && get_class($where["value"]) === "DateTime"
160
        ) {
161
            return $where["value"]->format("Y-m-d-H-i-s");
162
        }
163
164
        if (is_array((new Arr)->get($where, "values"))) {
165
            return implode("_", collect($where["values"])->flatten()->toArray());
166
        }
167
168
        if (is_array((new Arr)->get($where, "value"))) {
169
            return implode("_", collect($where["value"])->flatten()->toArray());
170
        }
171
172
        return (new Arr)->get($where, "value", "");
173
    }
174
175
    protected function getValuesFromBindings(array $where, string $values) : string
176
    {
177
        if (($this->query->bindings["where"][$this->currentBinding] ?? false) !== false) {
178
            $values = $this->query->bindings["where"][$this->currentBinding];
179
            $this->currentBinding++;
180
181
            if ($where["type"] === "between") {
182
                $values .= "_" . $this->query->bindings["where"][$this->currentBinding];
183
                $this->currentBinding++;
184
            }
185
        }
186
187
        if (is_object($values)
188
            && get_class($values) === "DateTime"
189
        ) {
190
            $values = $values->format("Y-m-d-H-i-s");
191
        }
192
193
        return $values;
194
    }
195
196
    protected function getWhereClauses(array $wheres = []) : string
197
    {
198
        return "" . $this->getWheres($wheres)
199
            ->reduce(function ($carry, $where) {
200
                $value = $carry;
201
                $value .= $this->getNestedClauses($where);
202
                $value .= $this->getColumnClauses($where);
203
                $value .= $this->getRawClauses($where);
204
                $value .= $this->getInAndNotInClauses($where);
205
                $value .= $this->getOtherClauses($where);
206
207
                return $value;
208
            });
209
    }
210
211
    protected function getNestedClauses(array $where) : string
212
    {
213
        if (! in_array($where["type"], ["Exists", "Nested", "NotExists"])) {
214
            return "";
215
        }
216
217
        return "-" . strtolower($where["type"]) . $this->getWhereClauses($where["query"]->wheres);
218
    }
219
220
    protected function getColumnClauses(array $where) : string
221
    {
222
        if ($where["type"] !== "Column") {
223
            return "";
224
        }
225
226
        return "-{$where["boolean"]}_{$where["first"]}_{$where["operator"]}_{$where["second"]}";
227
    }
228
229
    protected function getInAndNotInClauses(array $where) : string
230
    {
231
        if (! in_array($where["type"], ["In", "NotIn", "InRaw"])) {
232
            return "";
233
        }
234
235
        $type = strtolower($where["type"]);
236
        $subquery = $this->getValuesFromWhere($where);
237
        $values = collect($this->query->bindings["where"][$this->currentBinding] ?? []);
238
239
        if (Str::startsWith($subquery, $values->first())) {
240
            $this->currentBinding += count($where["values"]);
241
        }
242
243
        if (! is_numeric($subquery) && ! is_numeric(str_replace("_", "", $subquery))) {
244
            try {
245
                $subquery = Uuid::fromBytes($subquery);
246
                $values = $this->recursiveImplode([$subquery], "_");
247
248
                return "-{$where["column"]}_{$type}{$values}";
249
            } catch (Exception $exception) {
250
                // do nothing
251
            }
252
        }
253
254
        $subquery = preg_replace('/\?(?=(?:[^"]*"[^"]*")*[^"]*\Z)/m', "_??_", $subquery);
255
        $subquery = collect(vsprintf(str_replace("_??_", "%s", $subquery), $values->toArray()));
256
        $values = $this->recursiveImplode($subquery->toArray(), "_");
257
258
        return "-{$where["column"]}_{$type}{$values}";
259
    }
260
261
    protected function recursiveImplode(array $items, string $glue = ",") : string
262
    {
263
        $result = "";
264
265
        foreach ($items as $value) {
266
            if (is_string($value)) {
267
                $value = str_replace('"', '', $value);
268
                $value = explode(" ", $value);
269
270
                if (count($value) === 1) {
271
                    $value = $value[0];
272
                }
273
            }
274
275
            if (is_array($value)) {
276
                $result .= $this->recursiveImplode($value, $glue);
277
278
                continue;
279
            }
280
281
            $result .= $glue . $value;
282
        }
283
284
        return $result;
285
    }
286
287
    protected function getRawClauses(array $where) : string
288
    {
289
        if (! in_array($where["type"], ["raw"])) {
290
            return "";
291
        }
292
293
        $queryParts = explode("?", $where["sql"]);
294
        $clause = "_{$where["boolean"]}";
295
296
        while (count($queryParts) > 1) {
297
            $clause .= "_" . array_shift($queryParts);
298
            $clause .= $this->query->bindings["where"][$this->currentBinding];
299
            $this->currentBinding++;
300
        }
301
302
        $lastPart = array_shift($queryParts);
303
304
        if ($lastPart) {
305
            $clause .= "_" . $lastPart;
306
        }
307
308
        return "-" . str_replace(" ", "_", $clause);
309
    }
310
311
    protected function getOtherClauses(array $where) : string
312
    {
313
        if (in_array($where["type"], ["Exists", "Nested", "NotExists", "Column", "raw", "In", "NotIn", "InRaw"])) {
314
            return "";
315
        }
316
317
        $value = $this->getTypeClause($where);
318
        $value .= $this->getValuesClause($where);
319
320
        return "-{$where["column"]}_{$value}";
321
    }
322
323
    protected function getWheres(array $wheres) : Collection
324
    {
325
        $wheres = collect($wheres);
326
327
        if ($wheres->isEmpty()
328
            && property_exists($this->query, "wheres")
329
        ) {
330
            $wheres = collect($this->query->wheres);
331
        }
332
333
        return $wheres;
334
    }
335
336
    protected function getWithModels() : string
337
    {
338
        $eagerLoads = collect($this->eagerLoad);
339
340
        if ($eagerLoads->isEmpty()) {
341
            return "";
342
        }
343
344
        return $eagerLoads->keys()->reduce(function ($carry, $related) {
345
            if (! method_exists($this->model, $related)) {
346
                return "{$carry}-{$related}";
347
            }
348
349
            $relatedModel = $this->model->$related()->getRelated();
350
            $relatedConnection = $relatedModel->getConnection()->getName();
351
            $relatedDatabase = $relatedModel->getConnection()->getDatabaseName();
352
353
            return "{$carry}-{$relatedConnection}:{$relatedDatabase}:{$related}";
354
        });
355
    }
356
357
    protected function getBindingsSlug() : string
358
    {
359
        if (! method_exists($this->model, 'query')) {
360
            return '';
361
        }
362
        
363
        return Arr::query($this->model->query()->getBindings());
364
    }
365
}
366