Completed
Push — master ( 443aac...c0b066 )
by Abdelrahman
02:33
created

BaseRepository::executeCallback()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 2 Features 1
Metric Value
c 6
b 2
f 1
dl 0
loc 29
rs 8.439
cc 6
eloc 17
nc 10
nop 4
1
<?php
2
3
/*
4
 * NOTICE OF LICENSE
5
 *
6
 * Part of the Rinvex Repository Package.
7
 *
8
 * This source file is subject to The MIT License (MIT)
9
 * that is bundled with this package in the LICENSE file.
10
 *
11
 * Package: Rinvex Repository Package
12
 * License: The MIT License (MIT)
13
 * Link:    https://rinvex.com
14
 */
15
16
namespace Rinvex\Repository\Repositories;
17
18
use Closure;
19
use Illuminate\Contracts\Container\Container;
20
use Rinvex\Repository\Contracts\RepositoryContract;
21
22
abstract class BaseRepository implements RepositoryContract
23
{
24
    /**
25
     * The IoC container instance.
26
     *
27
     * @var \Illuminate\Contracts\Container\Container
28
     */
29
    protected $container;
30
31
    /**
32
     * The repository model.
33
     *
34
     * @var object
35
     */
36
    protected $model;
37
38
    /**
39
     * The repository identifier.
40
     *
41
     * @var string
42
     */
43
    protected $repositoryId;
44
45
    /**
46
     * Indicate if the repository cache is enabled.
47
     *
48
     * @var bool
49
     */
50
    protected $cacheEnabled = true;
51
52
    /**
53
     * Indicate if the repository cache clear is enabled.
54
     *
55
     * @var bool
56
     */
57
    protected $cacheClearEnabled = true;
58
59
    /**
60
     * The repository cache lifetime.
61
     *
62
     * @var int
63
     */
64
    protected $cacheLifetime;
65
66
    /**
67
     * The repository cache driver.
68
     *
69
     * @var string
70
     */
71
    protected $cacheDriver;
72
73
    /**
74
     * Execute given callback and cache result set.
75
     *
76
     * @param string   $class
77
     * @param string   $method
78
     * @param array    $args
79
     * @param \Closure $closure
80
     *
81
     * @return mixed
82
     */
83
    protected function executeCallback($class, $method, $args, Closure $closure)
84
    {
85
        $driver   = $this->getCacheDriver();
86
        $lifetime = $this->getCacheLifetime();
87
        $hash     = md5(json_encode($args + [$driver, $lifetime, $this->model->toSql()]));
88
        $cacheKey = $class.'@'.$method.'.'.$hash;
89
90
        if ($driver) {
91
            // Switch cache driver on runtime
92
            $this->getContainer('cache')->setDefaultDriver($driver);
93
        }
94
95
        if ($this->isCacheable()) {
96
            if (method_exists($this->getContainer('cache')->getStore(), 'tags')) {
97
                return $lifetime === -1
98
                    ? $this->getContainer('cache')->tags($this->getRepositoryId())->rememberForever($cacheKey, $closure)
99
                    : $this->getContainer('cache')->tags($this->getRepositoryId())->remember($cacheKey, $lifetime, $closure);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
100
            }
101
102
            // Store cache keys by mimicking cache tags
103
            $this->storeCacheKeys($class, $method, $hash);
104
105
            return $lifetime === -1
106
                ? $this->getContainer('cache')->rememberForever($cacheKey, $closure)
107
                : $this->getContainer('cache')->remember($cacheKey, $lifetime, $closure);
108
        }
109
110
        return call_user_func($closure);
111
    }
112
113
    /**
114
     * Set the IoC container instance.
115
     *
116
     * @param \Illuminate\Contracts\Container\Container $container
117
     *
118
     * @return $this
119
     */
120
    public function setContainer(Container $container)
121
    {
122
        $this->container = $container;
123
124
        return $this;
125
    }
126
127
    /**
128
     * Get the IoC container instance or any of it's services.
129
     *
130
     * @param string|null $service
131
     *
132
     * @return mixed
133
     */
134
    public function getContainer($service = null)
135
    {
136
        return is_null($service) ? ($this->container ?: app()) : ($this->container[$service] ?: app($service));
137
    }
138
139
    /**
140
     * Set the repository identifier.
141
     *
142
     * @param string $repositoryId
143
     *
144
     * @return $this
145
     */
146
    public function setRepositoryId($repositoryId)
147
    {
148
        $this->repositoryId = $repositoryId;
149
150
        return $this;
151
    }
152
153
    /**
154
     * Get the repository identifier.
155
     *
156
     * @return string
157
     */
158
    public function getRepositoryId()
159
    {
160
        return $this->repositoryId ?: get_called_class();
161
    }
162
163
    /**
164
     * Set the repository cache lifetime.
165
     *
166
     * @param int $cacheLifetime
167
     *
168
     * @return $this
169
     */
170
    public function setCacheLifetime($cacheLifetime)
171
    {
172
        $this->cacheLifetime = $cacheLifetime;
173
174
        return $this;
175
    }
176
177
    /**
178
     * Get the repository cache lifetime.
179
     *
180
     * @return int
181
     */
182
    public function getCacheLifetime()
183
    {
184
        return ! is_null($this->cacheLifetime)
185
            ? $this->cacheLifetime
186
            : $this->getContainer('config')->get('rinvex.repository.cache.lifetime');
187
    }
188
189
    /**
190
     * Set the repository cache driver.
191
     *
192
     * @param string $cacheDriver
193
     *
194
     * @return $this
195
     */
196
    public function setCacheDriver($cacheDriver)
197
    {
198
        $this->cacheDriver = $cacheDriver;
199
200
        return $this;
201
    }
202
203
    /**
204
     * Get the repository cache driver.
205
     *
206
     * @return string
207
     */
208
    public function getCacheDriver()
209
    {
210
        return $this->cacheDriver;
211
    }
212
213
    /**
214
     * Enable repository cache.
215
     *
216
     * @param bool $status
217
     *
218
     * @return $this
219
     */
220
    public function enableCache($status = true)
221
    {
222
        $this->cacheEnabled = $status;
223
224
        return $this;
225
    }
226
227
    /**
228
     * Determine if repository cache is enabled.
229
     *
230
     * @return bool
231
     */
232
    public function isCacheEnabled()
233
    {
234
        return $this->cacheEnabled;
235
    }
236
237
    /**
238
     * Enable repository cache clear.
239
     *
240
     * @param bool $status
241
     *
242
     * @return $this
243
     */
244
    public function enableCacheClear($status = true)
245
    {
246
        $this->cacheClearEnabled = $status;
247
248
        return $this;
249
    }
250
251
    /**
252
     * Determine if repository cache clear is enabled.
253
     *
254
     * @return bool
255
     */
256
    public function isCacheClearEnabled()
257
    {
258
        return $this->cacheClearEnabled;
259
    }
260
261
    /**
262
     * Forget the repository cache.
263
     *
264
     * @return $this
265
     */
266
    public function forgetCache()
267
    {
268
        if ($this->isCacheEnabled() && $this->getCacheLifetime()) {
269
            if (method_exists($this->getContainer('cache')->getStore(), 'tags')) {
270
                $this->getContainer('cache')->tags($this->getRepositoryId())->flush();
271
            } else {
272
                // Flush cache keys by mimicking cache tags
273
                foreach ($this->flushCacheKeys() as $cacheKey) {
274
                    $this->getContainer('cache')->forget($cacheKey);
275
                }
276
            }
277
278
            $this->getContainer('events')->fire($this->getRepositoryId().'.entity.cache.flushed', [$this]);
279
        }
280
281
        return $this;
282
    }
283
284
    /**
285
     * Set the relationships that should be eager loaded.
286
     *
287
     * @param mixed $relations
288
     *
289
     * @return $this
290
     */
291
    public function with($relations)
292
    {
293
        $this->model = $this->model->with($relations);
294
295
        return $this;
296
    }
297
298
    /**
299
     * Add an "order by" clause to the repository.
300
     *
301
     * @param string $column
302
     * @param string $direction
303
     *
304
     * @return $this
305
     */
306
    public function orderBy($column, $direction = 'asc')
307
    {
308
        $this->model = $this->model->orderBy($column, $direction);
309
310
        return $this;
311
    }
312
313
    /**
314
     * Register a new global scope.
315
     *
316
     * @param \Illuminate\Database\Eloquent\Scope|\Closure|string $scope
317
     * @param \Closure|null                                       $implementation
318
     *
319
     * @throws \InvalidArgumentException
320
     *
321
     * @return mixed
322
     */
323
    public function addGlobalScope($scope, Closure $implementation = null)
324
    {
325
        return $this->model->addGlobalScope($scope, $implementation);
326
    }
327
328
    /**
329
     * Remove all or passed registered global scopes.
330
     *
331
     * @param array|null $scopes
332
     *
333
     * @return $this
334
     */
335
    public function withoutGlobalScopes(array $scopes = null)
336
    {
337
        $this->model = $this->model->withoutGlobalScopes($scopes);
338
339
        return $this;
340
    }
341
342
    /**
343
     * Dynamically pass missing static methods to the model.
344
     *
345
     * @param $method
346
     * @param $parameters
347
     *
348
     * @return mixed
349
     */
350
    public static function __callStatic($method, $parameters)
351
    {
352
        return call_user_func_array([new static(), $method], $parameters);
353
    }
354
355
    /**
356
     * Dynamically pass missing methods to the model.
357
     *
358
     * @param string $method
359
     * @param array  $parameters
360
     *
361
     * @return mixed
362
     */
363
    public function __call($method, $parameters)
364
    {
365
        $model = $this->model;
366
367
        return call_user_func_array([$model, $method], $parameters);
368
    }
369
370
    /**
371
     * Store cache keys by mimicking cache tags.
372
     *
373
     * @param string $class
374
     * @param string $method
375
     * @param string $hash
376
     *
377
     * @return void
378
     */
379
    protected function storeCacheKeys($class, $method, $hash)
380
    {
381
        $keysFile  = $this->getContainer('config')->get('rinvex.repository.cache.keys_file');
382
        $cacheKeys = $this->getCacheKeys($keysFile);
383
384
        if (! isset($cacheKeys[$class]) || ! in_array($method.'.'.$hash, $cacheKeys[$class])) {
385
            $cacheKeys[$class][] = $method.'.'.$hash;
386
            file_put_contents($keysFile, json_encode($cacheKeys));
387
        }
388
    }
389
390
    /**
391
     * Flush cache keys by mimicking cache tags.
392
     *
393
     * @return array
394
     */
395
    protected function flushCacheKeys()
396
    {
397
        $flushedKeys  = [];
398
        $calledClasss = get_called_class();
399
        $config       = $this->getContainer('config')->get('rinvex.repository.cache');
400
        $cacheKeys    = $this->getCacheKeys($config['keys_file']);
401
402
        if (isset($cacheKeys[$calledClasss]) && is_array($cacheKeys[$calledClasss])) {
403
            foreach ($cacheKeys[$calledClasss] as $cacheKey) {
404
                $flushedKeys[] = $calledClasss.'@'.$cacheKey;
405
            }
406
407
            unset($cacheKeys[$calledClasss]);
408
            file_put_contents($config['keys_file'], json_encode($cacheKeys));
409
        }
410
411
        return $flushedKeys;
412
    }
413
414
    /**
415
     * Get cache keys.
416
     *
417
     * @param string $file
418
     *
419
     * @return array
420
     */
421
    protected function getCacheKeys($file)
422
    {
423
        return json_decode(file_get_contents(file_exists($file) ? $file : file_put_contents($file, null)), true) ?: [];
424
    }
425
426
    /**
427
     * Determine if repository is cacheable.
428
     *
429
     * @return bool
430
     */
431
    protected function isCacheable()
432
    {
433
        $skipUri = $this->getContainer('config')->get('rinvex.repository.cache.skip_uri');
434
435
        return $this->isCacheEnabled() && $this->getCacheLifetime()
436
               && ! $this->getContainer('request')->has($skipUri);
437
    }
438
}
439