1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Salah3id\Domains\Repository\Traits; |
4
|
|
|
|
5
|
|
|
use Illuminate\Contracts\Cache\Repository as CacheRepository; |
6
|
|
|
use Salah3id\Domains\Repository\Contracts\CriteriaInterface; |
7
|
|
|
use Salah3id\Domains\Repository\Helpers\CacheKeys; |
8
|
|
|
use ReflectionObject; |
9
|
|
|
use Exception; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Class CacheableRepository |
13
|
|
|
* @package Salah3id\Domains\Repository\Traits |
14
|
|
|
* @author Anderson Andrade <[email protected]> |
15
|
|
|
*/ |
16
|
|
|
trait CacheableRepository |
17
|
|
|
{ |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var CacheRepository |
21
|
|
|
*/ |
22
|
|
|
protected $cacheRepository = null; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Set Cache Repository |
26
|
|
|
* |
27
|
|
|
* @param CacheRepository $repository |
28
|
|
|
* |
29
|
|
|
* @return $this |
30
|
|
|
*/ |
31
|
|
|
public function setCacheRepository(CacheRepository $repository) |
32
|
|
|
{ |
33
|
|
|
$this->cacheRepository = $repository; |
34
|
|
|
|
35
|
|
|
return $this; |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Return instance of Cache Repository |
40
|
|
|
* |
41
|
|
|
* @return CacheRepository |
42
|
|
|
*/ |
43
|
|
|
public function getCacheRepository() |
44
|
|
|
{ |
45
|
|
|
if (is_null($this->cacheRepository)) { |
46
|
|
|
$this->cacheRepository = app(config('domains.repo-cache.repository', 'cache')); |
|
|
|
|
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
return $this->cacheRepository; |
|
|
|
|
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Skip Cache |
54
|
|
|
* |
55
|
|
|
* @param bool $status |
56
|
|
|
* |
57
|
|
|
* @return $this |
58
|
|
|
*/ |
59
|
|
|
public function skipCache($status = true) |
60
|
|
|
{ |
61
|
|
|
$this->cacheSkip = $status; |
|
|
|
|
62
|
|
|
|
63
|
|
|
return $this; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @return bool |
68
|
|
|
*/ |
69
|
|
|
public function isSkippedCache() |
70
|
|
|
{ |
71
|
|
|
$skipped = isset($this->cacheSkip) ? $this->cacheSkip : false; |
72
|
|
|
$request = app('Illuminate\Http\Request'); |
73
|
|
|
$skipCacheParam = config('domains.repo-cache.params.skipCache', 'skipCache'); |
74
|
|
|
|
75
|
|
|
if ($request->has($skipCacheParam) && $request->get($skipCacheParam)) { |
76
|
|
|
$skipped = true; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
return $skipped; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @param $method |
84
|
|
|
* |
85
|
|
|
* @return bool |
86
|
|
|
*/ |
87
|
|
|
protected function allowedCache($method) |
88
|
|
|
{ |
89
|
|
|
$cacheEnabled = config('domains.repo-cache.enabled', true); |
90
|
|
|
|
91
|
|
|
if (!$cacheEnabled) { |
92
|
|
|
return false; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
$cacheOnly = isset($this->cacheOnly) ? $this->cacheOnly : config('domains.repo-cache.allowed.only', null); |
96
|
|
|
$cacheExcept = isset($this->cacheExcept) ? $this->cacheExcept : config('domains.repo-cache.allowed.except', null); |
97
|
|
|
|
98
|
|
|
if (is_array($cacheOnly)) { |
99
|
|
|
return in_array($method, $cacheOnly); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
if (is_array($cacheExcept)) { |
103
|
|
|
return !in_array($method, $cacheExcept); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
if (is_null($cacheOnly) && is_null($cacheExcept)) { |
107
|
|
|
return true; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return false; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Get Cache key for the method |
115
|
|
|
* |
116
|
|
|
* @param $method |
117
|
|
|
* @param $args |
118
|
|
|
* |
119
|
|
|
* @return string |
120
|
|
|
*/ |
121
|
|
|
public function getCacheKey($method, $args = null) |
122
|
|
|
{ |
123
|
|
|
|
124
|
|
|
$request = app('Illuminate\Http\Request'); |
125
|
|
|
$args = serialize($args); |
126
|
|
|
$criteria = $this->serializeCriteria(); |
127
|
|
|
$key = sprintf('%s@%s-%s', get_called_class(), $method, md5($args . $criteria . $request->fullUrl())); |
128
|
|
|
|
129
|
|
|
CacheKeys::putKey(get_called_class(), $key); |
130
|
|
|
|
131
|
|
|
return $key; |
132
|
|
|
|
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Serialize the criteria making sure the Closures are taken care of. |
137
|
|
|
* |
138
|
|
|
* @return string |
139
|
|
|
*/ |
140
|
|
|
protected function serializeCriteria() |
141
|
|
|
{ |
142
|
|
|
try { |
143
|
|
|
return serialize($this->getCriteria()); |
|
|
|
|
144
|
|
|
} catch (Exception $e) { |
145
|
|
|
return serialize($this->getCriteria()->map(function ($criterion) { |
146
|
|
|
return $this->serializeCriterion($criterion); |
147
|
|
|
})); |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Serialize single criterion with customized serialization of Closures. |
153
|
|
|
* |
154
|
|
|
* @param \Salah3id\Domains\Repository\Contracts\CriteriaInterface $criterion |
155
|
|
|
* @return \Salah3id\Domains\Repository\Contracts\CriteriaInterface|array |
156
|
|
|
* |
157
|
|
|
* @throws \Exception |
158
|
|
|
*/ |
159
|
|
|
protected function serializeCriterion($criterion) |
160
|
|
|
{ |
161
|
|
|
try { |
162
|
|
|
serialize($criterion); |
163
|
|
|
|
164
|
|
|
return $criterion; |
165
|
|
|
} catch (Exception $e) { |
166
|
|
|
// We want to take care of the closure serialization errors, |
167
|
|
|
// other than that we will simply re-throw the exception. |
168
|
|
|
if ($e->getMessage() !== "Serialization of 'Closure' is not allowed") { |
169
|
|
|
throw $e; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
$r = new ReflectionObject($criterion); |
173
|
|
|
|
174
|
|
|
return [ |
175
|
|
|
'hash' => md5((string) $r), |
176
|
|
|
]; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Get cache time |
182
|
|
|
* |
183
|
|
|
* Return minutes: version < 5.8 |
184
|
|
|
* Return seconds: version >= 5.8 |
185
|
|
|
* |
186
|
|
|
* @return int |
187
|
|
|
*/ |
188
|
|
|
public function getCacheTime() |
189
|
|
|
{ |
190
|
|
|
$cacheMinutes = isset($this->cacheMinutes) ? $this->cacheMinutes : config('domains.repo-cache.minutes', 30); |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* https://laravel.com/docs/5.8/upgrade#cache-ttl-in-seconds |
194
|
|
|
*/ |
195
|
|
|
if ($this->versionCompare($this->app->version(), "5.7.*", ">")) { |
|
|
|
|
196
|
|
|
return $cacheMinutes * 60; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
return $cacheMinutes; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Retrieve all data of repository |
204
|
|
|
* |
205
|
|
|
* @param array $columns |
206
|
|
|
* |
207
|
|
|
* @return mixed |
208
|
|
|
*/ |
209
|
|
|
public function all($columns = ['*']) |
210
|
|
|
{ |
211
|
|
|
if (!$this->allowedCache('all') || $this->isSkippedCache()) { |
212
|
|
|
return parent::all($columns); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
$key = $this->getCacheKey('all', func_get_args()); |
216
|
|
|
$time = $this->getCacheTime(); |
217
|
|
|
$value = $this->getCacheRepository()->remember($key, $time, function () use ($columns) { |
218
|
|
|
return parent::all($columns); |
219
|
|
|
}); |
220
|
|
|
|
221
|
|
|
$this->resetModel(); |
|
|
|
|
222
|
|
|
$this->resetScope(); |
|
|
|
|
223
|
|
|
return $value; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Retrieve all data of repository, paginated |
228
|
|
|
* |
229
|
|
|
* @param null $limit |
|
|
|
|
230
|
|
|
* @param array $columns |
231
|
|
|
* @param string $method |
232
|
|
|
* |
233
|
|
|
* @return mixed |
234
|
|
|
*/ |
235
|
|
|
public function paginate($limit = null, $columns = ['*'], $method = 'paginate') |
236
|
|
|
{ |
237
|
|
|
if (!$this->allowedCache('paginate') || $this->isSkippedCache()) { |
238
|
|
|
return parent::paginate($limit, $columns, $method); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
$key = $this->getCacheKey('paginate', func_get_args()); |
242
|
|
|
|
243
|
|
|
$time = $this->getCacheTime(); |
244
|
|
|
$value = $this->getCacheRepository()->remember($key, $time, function () use ($limit, $columns, $method) { |
245
|
|
|
return parent::paginate($limit, $columns, $method); |
246
|
|
|
}); |
247
|
|
|
|
248
|
|
|
$this->resetModel(); |
249
|
|
|
$this->resetScope(); |
250
|
|
|
return $value; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Find data by id |
255
|
|
|
* |
256
|
|
|
* @param $id |
257
|
|
|
* @param array $columns |
258
|
|
|
* |
259
|
|
|
* @return mixed |
260
|
|
|
*/ |
261
|
|
|
public function find($id, $columns = ['*']) |
262
|
|
|
{ |
263
|
|
|
if (!$this->allowedCache('find') || $this->isSkippedCache()) { |
264
|
|
|
return parent::find($id, $columns); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
$key = $this->getCacheKey('find', func_get_args()); |
268
|
|
|
$time = $this->getCacheTime(); |
269
|
|
|
$value = $this->getCacheRepository()->remember($key, $time, function () use ($id, $columns) { |
270
|
|
|
return parent::find($id, $columns); |
271
|
|
|
}); |
272
|
|
|
|
273
|
|
|
$this->resetModel(); |
274
|
|
|
$this->resetScope(); |
275
|
|
|
return $value; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Find data by field and value |
280
|
|
|
* |
281
|
|
|
* @param $field |
282
|
|
|
* @param $value |
283
|
|
|
* @param array $columns |
284
|
|
|
* |
285
|
|
|
* @return mixed |
286
|
|
|
*/ |
287
|
|
|
public function findByField($field, $value = null, $columns = ['*']) |
288
|
|
|
{ |
289
|
|
|
if (!$this->allowedCache('findByField') || $this->isSkippedCache()) { |
290
|
|
|
return parent::findByField($field, $value, $columns); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$key = $this->getCacheKey('findByField', func_get_args()); |
294
|
|
|
$time = $this->getCacheTime(); |
295
|
|
|
$value = $this->getCacheRepository()->remember($key, $time, function () use ($field, $value, $columns) { |
296
|
|
|
return parent::findByField($field, $value, $columns); |
297
|
|
|
}); |
298
|
|
|
|
299
|
|
|
$this->resetModel(); |
300
|
|
|
$this->resetScope(); |
301
|
|
|
return $value; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* Find data by multiple fields |
306
|
|
|
* |
307
|
|
|
* @param array $where |
308
|
|
|
* @param array $columns |
309
|
|
|
* |
310
|
|
|
* @return mixed |
311
|
|
|
*/ |
312
|
|
|
public function findWhere(array $where, $columns = ['*']) |
313
|
|
|
{ |
314
|
|
|
if (!$this->allowedCache('findWhere') || $this->isSkippedCache()) { |
315
|
|
|
return parent::findWhere($where, $columns); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
$key = $this->getCacheKey('findWhere', func_get_args()); |
319
|
|
|
$time = $this->getCacheTime(); |
320
|
|
|
$value = $this->getCacheRepository()->remember($key, $time, function () use ($where, $columns) { |
321
|
|
|
return parent::findWhere($where, $columns); |
322
|
|
|
}); |
323
|
|
|
|
324
|
|
|
$this->resetModel(); |
325
|
|
|
$this->resetScope(); |
326
|
|
|
return $value; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Find data by Criteria |
331
|
|
|
* |
332
|
|
|
* @param CriteriaInterface $criteria |
333
|
|
|
* |
334
|
|
|
* @return mixed |
335
|
|
|
*/ |
336
|
|
|
public function getByCriteria(CriteriaInterface $criteria) |
337
|
|
|
{ |
338
|
|
|
if (!$this->allowedCache('getByCriteria') || $this->isSkippedCache()) { |
339
|
|
|
return parent::getByCriteria($criteria); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
$key = $this->getCacheKey('getByCriteria', func_get_args()); |
343
|
|
|
$time = $this->getCacheTime(); |
344
|
|
|
$value = $this->getCacheRepository()->remember($key, $time, function () use ($criteria) { |
345
|
|
|
return parent::getByCriteria($criteria); |
346
|
|
|
}); |
347
|
|
|
|
348
|
|
|
$this->resetModel(); |
349
|
|
|
$this->resetScope(); |
350
|
|
|
return $value; |
351
|
|
|
} |
352
|
|
|
} |
353
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.