Passed
Push — master ( e200e0...bf70a1 )
by Mike
02:28
created

Caching::__call()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 2
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php namespace GeneaLabs\LaravelModelCaching\Traits;
2
3
use Carbon\Carbon;
4
use Closure;
5
use GeneaLabs\LaravelModelCaching\CachedBuilder;
6
use GeneaLabs\LaravelModelCaching\CacheKey;
7
use GeneaLabs\LaravelModelCaching\CacheTags;
8
use Illuminate\Cache\TaggableStore;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Database\Eloquent\Scope;
11
use Illuminate\Database\Query\Builder;
12
13
trait Caching
14
{
15
    protected $isCachable = true;
16
    protected $scopesAreApplied = false;
17
    protected $macroKey = "";
18
19
    public function __call($method, $parameters)
20
    {
21
        $result = parent::__call($method, $parameters);
22
23
        if (isset($this->localMacros[$method])) {
24
            $this->macroKey .= "-{$method}";
25
26
            if ($parameters) {
27
                $this->macroKey .= implode("_", $parameters);
28
            }
29
        }
30
31
        return $result;
32
    }
33
34
    protected function applyScopesToInstance()
35
    {
36
        if (! property_exists($this, "scopes")
37
            || $this->scopesAreApplied
38
        ) {
39
            return;
40
        }
41
42
        foreach ($this->scopes as $identifier => $scope) {
43
            if (! isset($this->scopes[$identifier])) {
44
                continue;
45
            }
46
47
            $this->callScope(function () use ($scope) {
1 ignored issue
show
Bug introduced by
The method callScope() does not exist on GeneaLabs\LaravelModelCaching\Traits\Caching. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

47
            $this->/** @scrutinizer ignore-call */ 
48
                   callScope(function () use ($scope) {
Loading history...
48
                if ($scope instanceof Closure) {
49
                    $scope($this);
50
                }
51
52
                if ($scope instanceof Scope
53
                    && $this instanceof CachedBuilder
54
                ) {
55
                    $scope->apply($this, $this->getModel());
56
                }
57
            });
58
        }
59
60
        $this->scopesAreApplied = true;
61
    }
62
63
    public function cache(array $tags = [])
64
    {
65
        $cache = app('cache');
66
67
        if (config('laravel-model-caching.store')) {
68
            $cache = $cache->store(config('laravel-model-caching.store'));
69
        }
70
71
        if (is_subclass_of($cache->getStore(), TaggableStore::class)) {
72
            $cache = $cache->tags($tags);
73
        }
74
75
        return $cache;
76
    }
77
78
    public function disableModelCaching()
79
    {
80
        $this->isCachable = false;
81
82
        return $this;
83
    }
84
85
    public function flushCache(array $tags = [])
86
    {
87
        if (count($tags) === 0) {
88
            $tags = $this->makeCacheTags();
89
        }
90
91
        $this->cache($tags)->flush();
92
93
        [$cacheCooldown] = $this->getModelCacheCooldown($this);
94
95
        if ($cacheCooldown) {
96
            $cachePrefix = $this->getCachePrefix();
97
            $modelClassName = get_class($this);
98
            $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
99
100
            $this->cache()
101
                ->rememberForever($cacheKey, function () {
102
                    return (new Carbon)->now();
103
                });
104
        }
105
    }
106
107
    protected function getCachePrefix() : string
108
    {
109
        $cachePrefix = config("laravel-model-caching.cache-prefix", "");
110
111
        if ($this->model
112
            && property_exists($this->model, "cachePrefix")
113
        ) {
114
            $cachePrefix = $this->model->cachePrefix;
115
        }
116
117
        $cachePrefix = $cachePrefix
118
            ? "{$cachePrefix}:"
119
            : "";
120
121
        return "genealabs:laravel-model-caching:"
122
            . $cachePrefix;
123
    }
124
125
    protected function makeCacheKey(
126
        array $columns = ['*'],
127
        $idColumn = null,
128
        string $keyDifferentiator = ''
129
    ) : string {
130
        $this->applyScopesToInstance();
131
        $eagerLoad = $this->eagerLoad ?? [];
132
        $model = $this;
133
134
        if (property_exists($this, "model")) {
135
            $model = $this->model;
136
        }
137
138
        if (method_exists($this, "getModel")) {
139
            $model = $this->getModel();
1 ignored issue
show
Bug introduced by
The method getModel() does not exist on GeneaLabs\LaravelModelCaching\Traits\Caching. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

139
            /** @scrutinizer ignore-call */ 
140
            $model = $this->getModel();
Loading history...
140
        }
141
142
        $query = $this->query
143
            ?? app('db')->query();
144
        
145
        if ($this->query
146
            && method_exists($this->query, "getQuery")
147
        ) {
148
            $query = $this->query->getQuery();
149
        }
150
151
        return (new CacheKey($eagerLoad, $model, $query, $this->macroKey))
152
            ->make($columns, $idColumn, $keyDifferentiator);
153
    }
154
155
    protected function makeCacheTags() : array
156
    {
157
        $eagerLoad = $this->eagerLoad ?? [];
158
        $model = $this->getModel() instanceof Model
159
            ? $this->getModel()
160
            : $this;
161
        $query = $this->query instanceof Builder
162
            ? $this->query
163
            : app('db')->query();
164
        $tags = (new CacheTags($eagerLoad, $model, $query))
165
            ->make();
166
167
        return $tags;
168
    }
169
170
    public function getModelCacheCooldown(Model $instance) : array
171
    {
172
        if (! $instance->cacheCooldownSeconds) {
173
            return [null, null, null];
174
        }
175
176
        $cachePrefix = $this->getCachePrefix();
177
        $modelClassName = get_class($instance);
178
        [$cacheCooldown, $invalidatedAt, $savedAt] = $this
179
            ->getCacheCooldownDetails($instance, $cachePrefix, $modelClassName);
180
181
        if (! $cacheCooldown || $cacheCooldown === 0) {
182
            return [null, null, null];
183
        }
184
185
        return [$cacheCooldown, $invalidatedAt, $savedAt];
186
    }
187
188
    protected function getCacheCooldownDetails(
189
        Model $instance,
190
        string $cachePrefix,
191
        string $modelClassName
192
    ) : array {
193
        return [
194
            $instance
195
                ->cache()
196
                ->get("{$cachePrefix}:{$modelClassName}-cooldown:seconds"),
197
            $instance
198
                ->cache()
199
                ->get("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at"),
200
            $instance
201
                ->cache()
202
                ->get("{$cachePrefix}:{$modelClassName}-cooldown:saved-at"),
203
        ];
204
    }
205
206
    protected function checkCooldownAndRemoveIfExpired(Model $instance)
207
    {
208
        [$cacheCooldown, $invalidatedAt] = $this->getModelCacheCooldown($instance);
209
210
        if (! $cacheCooldown
211
            || (new Carbon)->now()->diffInSeconds($invalidatedAt) < $cacheCooldown
212
        ) {
213
            return;
214
        }
215
216
        $cachePrefix = $this->getCachePrefix();
217
        $modelClassName = get_class($instance);
218
219
        $instance
220
            ->cache()
221
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:seconds");
222
        $instance
223
            ->cache()
224
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
225
        $instance
226
            ->cache()
227
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
228
        $instance->flushCache();
229
    }
230
231
    protected function checkCooldownAndFlushAfterPersisting(Model $instance, string $relationship = "")
232
    {
233
        [$cacheCooldown, $invalidatedAt] = $instance->getModelCacheCooldown($instance);
234
235
        if (! $cacheCooldown) {
236
            $instance->flushCache();
237
238
            if ($relationship) {
239
                $relationshipInstance = $instance->$relationship()->getModel();
240
241
                if (method_exists($instance, "flushCache")) {
242
                    $relationshipInstance->flushCache();
243
                }
244
            }
245
246
            return;
247
        }
248
249
        $this->setCacheCooldownSavedAtTimestamp($instance);
250
251
        if ((new Carbon)->now()->diffInSeconds($invalidatedAt) >= $cacheCooldown) {
252
            $instance->flushCache();
253
254
            if ($relationship) {
255
                $instance->$relationship()->getModel()->flushCache();
256
            }
257
        }
258
    }
259
260
    public function isCachable() : bool
261
    {
262
        return $this->isCachable
263
            && ! config('laravel-model-caching.disabled');
264
    }
265
266
    protected function setCacheCooldownSavedAtTimestamp(Model $instance)
267
    {
268
        $cachePrefix = $this->getCachePrefix();
269
        $modelClassName = get_class($instance);
270
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
271
272
        $instance->cache()
273
            ->rememberForever($cacheKey, function () {
274
                return (new Carbon)->now();
275
            });
276
    }
277
}
278