Passed
Push — master ( 6e54e7...2eaef0 )
by Mike
02:21
created

CacheKey::getValuesClause()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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