Passed
Push — master ( a1b5a1...3d59b3 )
by Mike
02:21
created

Cachable::checkCooldownAndFlushAfterPersiting()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.439
c 0
b 0
f 0
cc 6
eloc 17
nc 7
nop 1
1
<?php namespace GeneaLabs\LaravelModelCaching\Traits;
2
3
use GeneaLabs\LaravelModelCaching\CachedBuilder;
4
use GeneaLabs\LaravelModelCaching\CachedModel;
5
use GeneaLabs\LaravelModelCaching\CacheKey;
6
use GeneaLabs\LaravelModelCaching\CacheTags;
7
use Illuminate\Cache\TaggableStore;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
10
use Illuminate\Database\Query\Builder;
11
12
trait Cachable
13
{
14
    protected $isCachable = true;
15
16
    public function cache(array $tags = [])
17
    {
18
        $cache = cache();
19
20
        if (config('laravel-model-caching.store')) {
21
            $cache = $cache->store(config('laravel-model-caching.store'));
22
        }
23
24
        if (is_subclass_of($cache->getStore(), TaggableStore::class)) {
25
            $tags = $this->addTagsWhenCalledFromCachedBuilder($tags);
26
            $cache = $cache->tags($tags);
27
        }
28
29
        return $cache;
30
    }
31
32
    protected function addTagsWhenCalledFromCachedBuilder(array $tags) : array
33
    {
34
        $usesCachableTrait = collect(class_uses($this))
35
            ->contains("GeneaLabs\LaravelModelCaching\Traits\Cachable");
36
37
        if (! $usesCachableTrait) {
38
            array_push($tags, str_slug(get_called_class()));
39
        }
40
41
        return $tags;
42
    }
43
44
    public function disableCache()
45
    {
46
        $this->isCachable = false;
47
48
        return $this;
49
    }
50
51
    public function flushCache(array $tags = [])
52
    {
53
        if (count($tags) === 0) {
54
            $tags = $this->makeCacheTags();
55
        }
56
57
        $this->cache($tags)->flush();
58
59
        [$cacheCooldown, $invalidatedAt, $savedAt] = $this->getModelCacheCooldown($this);
1 ignored issue
show
Bug introduced by
$this of type GeneaLabs\LaravelModelCaching\Traits\Cachable is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $instance of GeneaLabs\LaravelModelCa...getModelCacheCooldown(). ( Ignorable by Annotation )

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

59
        [$cacheCooldown, $invalidatedAt, $savedAt] = $this->getModelCacheCooldown(/** @scrutinizer ignore-type */ $this);
Loading history...
60
61
        if ($cacheCooldown) {
62
            $cachePrefix = "genealabs:laravel-model-caching:"
63
                . (config('laravel-model-caching.cache-prefix')
64
                    ? config('laravel-model-caching.cache-prefix', '') . ":"
65
                    : "");
66
            $modelClassName = get_class($this);
67
            $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
68
69
            $this->cache()
70
                ->rememberForever($cacheKey, function () {
71
                    return now();
72
                });
73
        }
74
    }
75
76
    protected function makeCacheKey(
77
        array $columns = ['*'],
78
        $idColumn = null,
79
        string $keyDifferentiator = ''
80
    ) : string {
81
        $eagerLoad = $this->eagerLoad ?? [];
82
        $model = $this->model ?? $this;
83
        $query = $this->query ?? app(Builder::class);
1 ignored issue
show
Bug Best Practice introduced by
The property query does not exist on GeneaLabs\LaravelModelCaching\Traits\Cachable. Did you maybe forget to declare it?
Loading history...
84
85
        return (new CacheKey($eagerLoad, $model, $query))
1 ignored issue
show
Bug introduced by
It seems like $model can also be of type GeneaLabs\LaravelModelCaching\Traits\Cachable; however, parameter $model of GeneaLabs\LaravelModelCa...CacheKey::__construct() does only seem to accept Illuminate\Database\Eloquent\Model, maybe add an additional type check? ( Ignorable by Annotation )

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

85
        return (new CacheKey($eagerLoad, /** @scrutinizer ignore-type */ $model, $query))
Loading history...
86
            ->make($columns, $idColumn, $keyDifferentiator);
87
    }
88
89
    protected function makeCacheTags() : array
90
    {
91
        $tags = (new CacheTags($this->eagerLoad ?? [], $this->model ?? $this))
1 ignored issue
show
Bug introduced by
It seems like $this->model ?? $this can also be of type GeneaLabs\LaravelModelCaching\Traits\Cachable; however, parameter $model of GeneaLabs\LaravelModelCa...acheTags::__construct() does only seem to accept Illuminate\Database\Eloquent\Model, maybe add an additional type check? ( Ignorable by Annotation )

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

91
        $tags = (new CacheTags($this->eagerLoad ?? [], /** @scrutinizer ignore-type */ $this->model ?? $this))
Loading history...
92
            ->make();
93
94
        return $tags;
95
    }
96
97
    protected function getModelCacheCooldown(Model $instance)
98
    {
99
        $cachePrefix = "genealabs:laravel-model-caching:"
100
            . (config('laravel-model-caching.cache-prefix')
101
                ? config('laravel-model-caching.cache-prefix', '') . ":"
102
                : "");
103
        $modelClassName = get_class($instance);
104
105
        $cacheCooldown = $instance
106
            ->cache()
107
            ->get("{$cachePrefix}:{$modelClassName}-cooldown:seconds");
1 ignored issue
show
Bug introduced by
EncapsedNode of type string is incompatible with the type array expected by parameter $columns of Illuminate\Database\Eloquent\Builder::get(). ( Ignorable by Annotation )

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

107
            ->get(/** @scrutinizer ignore-type */ "{$cachePrefix}:{$modelClassName}-cooldown:seconds");
Loading history...
108
109
        if (! $cacheCooldown) {
110
            return [null, null, null];
111
        }
112
113
        $invalidatedAt = $instance
114
            ->cache()
115
            ->get("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
116
117
        $savedAt = $instance
118
            ->cache()
119
            ->get("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
120
121
        return [
122
            $cacheCooldown,
123
            $invalidatedAt,
124
            $savedAt,
125
        ];
126
    }
127
128
    protected function checkCooldownAndRemoveIfExpired(Model $instance)
129
    {
130
        [$cacheCooldown, $invalidatedAt] = $this->getModelCacheCooldown($instance);
131
132
        if (! $cacheCooldown) {
133
            return;
134
        }
135
136
        if (now()->diffInSeconds($invalidatedAt) >= $cacheCooldown) {
137
            $cachePrefix = "genealabs:laravel-model-caching:"
138
                . (config('laravel-model-caching.cache-prefix')
139
                    ? config('laravel-model-caching.cache-prefix', '') . ":"
140
                    : "");
141
            $modelClassName = get_class($instance);
142
143
            $instance
144
                ->cache()
145
                ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
146
            $instance
147
                ->cache()
148
                ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
149
            $instance
150
                ->cache()
151
                ->forget("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
152
            $instance->flushCache();
153
        }
154
    }
155
156
    protected function checkCooldownAndFlushAfterPersiting(Model $instance)
157
    {
158
        [$cacheCooldown, $invalidatedAt, $savedAt] = $instance->getModelCacheCooldown($instance);
159
160
        if (! $cacheCooldown) {
161
            $instance->flushCache();
162
163
            return;
164
        }
165
166
        if ($cacheCooldown) {
167
            $cachePrefix = "genealabs:laravel-model-caching:"
168
                . (config('laravel-model-caching.cache-prefix')
169
                    ? config('laravel-model-caching.cache-prefix', '') . ":"
170
                    : "");
171
            $modelClassName = get_class($instance);
172
            $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
173
174
            $instance->cache()
175
                ->rememberForever($cacheKey, function () {
176
                    return now();
177
                });
178
        }
179
180
        if ($savedAt > $invalidatedAt
181
                && now()->diffInSeconds($invalidatedAt) >= $cacheCooldown
182
        ) {
183
            $instance->flushCache();
184
        }
185
    }
186
187
    public static function bootCachable()
188
    {
189
        // TODO: add for deleted,updated,etc?
190
        static::saved(function ($instance) {
191
            $instance->checkCooldownAndFlushAfterPersiting($instance);
192
        });
193
    }
194
195
    public static function all($columns = ['*'])
196
    {
197
        if (config('laravel-model-caching.disabled')) {
198
            return parent::all($columns);
199
        }
200
201
        $class = get_called_class();
202
        $instance = new $class;
203
        $tags = [str_slug(get_called_class())];
204
        $key = $instance->makeCacheKey();
205
206
        return $instance->cache($tags)
207
            ->rememberForever($key, function () use ($columns) {
208
                return parent::all($columns);
209
            });
210
    }
211
212
    public function newEloquentBuilder($query)
213
    {
214
        if (! $this->isCachable()) {
215
            $this->isCachable = true;
216
217
            return new EloquentBuilder($query);
218
        }
219
220
        return new CachedBuilder($query);
221
    }
222
223
    public function isCachable() : bool
224
    {
225
        return $this->isCachable
226
            && ! config('laravel-model-caching.disabled');
227
    }
228
229
    public function scopeWithCacheCooldownSeconds(
230
        EloquentBuilder $query,
231
        int $seconds
232
    ) : EloquentBuilder {
233
        $cachePrefix = "genealabs:laravel-model-caching:"
234
            . (config('laravel-model-caching.cache-prefix')
235
                ? config('laravel-model-caching.cache-prefix', '') . ":"
236
                : "");
237
        $modelClassName = get_class($this);
238
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:seconds";
239
240
        $this->cache()
241
            ->rememberForever($cacheKey, function () use ($seconds) {
242
                return $seconds;
243
            });
244
245
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at";
246
        $this->cache()
247
            ->rememberForever($cacheKey, function () use ($seconds) {
0 ignored issues
show
Unused Code introduced by
The import $seconds is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
248
                return now();
249
            });
250
251
        return $query;
252
    }
253
}
254