Completed
Push — master ( 0a6f07...c21155 )
by Abdelrahman
06:08 queued 03:43
created

BaseRepository::executeCallback()   C

Complexity

Conditions 7
Paths 20

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 2 Features 1
Metric Value
c 4
b 2
f 1
dl 0
loc 28
rs 6.7272
cc 7
eloc 16
nc 20
nop 6
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
     * Execute given callback and cache result set.
61
     *
62
     * @param string   $class
63
     * @param string   $method
64
     * @param string   $hash
65
     * @param \Closure $closure
66
     * @param int      $lifetime
0 ignored issues
show
Documentation introduced by
Should the type for parameter $lifetime not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
67
     * @param string   $driver
0 ignored issues
show
Documentation introduced by
Should the type for parameter $driver not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
68
     *
69
     * @return mixed
70
     */
71
    protected function executeCallback($class, $method, $hash, $lifetime = null, $driver = null, Closure $closure)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
72
    {
73
        $cacheKey = $class.'@'.$method.'.'.$hash;
74
        $config   = $this->getContainer('config')->get('rinvex.repository.cache');
75
        $lifetime = $lifetime ?: $config['lifetime'];
76
77
        // Switch cache driver on runtime
78
        if ($driver) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $driver of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

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