Passed
Push — master ( c7d0c7...a81fec )
by Mike
02:17
created

Cachable::bootCachable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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] = $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] = $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 getCachePrefix() : string
77
    {
78
        return "genealabs:laravel-model-caching:"
79
            . (config('laravel-model-caching.cache-prefix')
80
                ? config('laravel-model-caching.cache-prefix', '') . ":"
81
                : "");
82
    }
83
84
    protected function makeCacheKey(
85
        array $columns = ['*'],
86
        $idColumn = null,
87
        string $keyDifferentiator = ''
88
    ) : string {
89
        $eagerLoad = $this->eagerLoad ?? [];
90
        $model = $this->model ?? $this;
91
        $query = $this->query ?? app(Builder::class);
92
93
        return (new CacheKey($eagerLoad, $model, $query))
94
            ->make($columns, $idColumn, $keyDifferentiator);
95
    }
96
97
    protected function makeCacheTags() : array
98
    {
99
        $tags = (new CacheTags($this->eagerLoad ?? [], $this->model ?? $this))
100
            ->make();
101
102
        return $tags;
103
    }
104
105
    protected function getModelCacheCooldown(Model $instance)
106
    {
107
        $cachePrefix = $this->getCachePrefix();
108
        $modelClassName = get_class($instance);
109
110
        $cacheCooldown = $instance
111
            ->cache()
112
            ->get("{$cachePrefix}:{$modelClassName}-cooldown:seconds");
113
114
        if (! $cacheCooldown) {
115
            return [null, null, null];
116
        }
117
118
        $invalidatedAt = $instance
119
            ->cache()
120
            ->get("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
121
122
        $savedAt = $instance
123
            ->cache()
124
            ->get("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
125
126
        return [
127
            $cacheCooldown,
128
            $invalidatedAt,
129
            $savedAt,
130
        ];
131
    }
132
133
    protected function checkCooldownAndRemoveIfExpired(Model $instance)
134
    {
135
        [$cacheCooldown, $invalidatedAt] = $this->getModelCacheCooldown($instance);
136
137
        if (! $cacheCooldown
138
            || now()->diffInSeconds($invalidatedAt) < $cacheCooldown
139
        ) {
140
            return;
141
        }
142
143
        $cachePrefix = $this->getCachePrefix();
144
        $modelClassName = get_class($instance);
145
146
        $instance
147
            ->cache()
148
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
149
        $instance
150
            ->cache()
151
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
152
        $instance
153
            ->cache()
154
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
155
        $instance->flushCache();
156
    }
157
158
    protected function checkCooldownAndFlushAfterPersiting(Model $instance)
159
    {
160
        [$cacheCooldown, $invalidatedAt, $savedAt] = $instance->getModelCacheCooldown($instance);
161
162
        if (! $cacheCooldown) {
163
            $instance->flushCache();
164
165
            return;
166
        }
167
168
        $this->setCacheCooldownSavedAtTimestamp($instance);
169
170
        if ($savedAt > $invalidatedAt
171
                && now()->diffInSeconds($invalidatedAt) >= $cacheCooldown
172
        ) {
173
            $instance->flushCache();
174
        }
175
    }
176
177
    protected function setCacheCooldownSavedAtTimestamp(Model $instance)
178
    {
179
        $cachePrefix = "genealabs:laravel-model-caching:"
180
            . (config('laravel-model-caching.cache-prefix')
181
                ? config('laravel-model-caching.cache-prefix', '') . ":"
182
                : "");
183
        $modelClassName = get_class($instance);
184
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
185
186
        $instance->cache()
187
            ->rememberForever($cacheKey, function () {
188
                return now();
189
            });
190
    }
191
192
    public static function bootCachable()
193
    {
194
        // TODO: add for deleted,updated,etc?
195
        static::saved(function ($instance) {
196
            $instance->checkCooldownAndFlushAfterPersiting($instance);
197
        });
198
    }
199
200
    public static function all($columns = ['*'])
201
    {
202
        if (config('laravel-model-caching.disabled')) {
203
            return parent::all($columns);
204
        }
205
206
        $class = get_called_class();
207
        $instance = new $class;
208
        $tags = [str_slug(get_called_class())];
209
        $key = $instance->makeCacheKey();
210
211
        return $instance->cache($tags)
212
            ->rememberForever($key, function () use ($columns) {
213
                return parent::all($columns);
214
            });
215
    }
216
217
    public function newEloquentBuilder($query)
218
    {
219
        if (! $this->isCachable()) {
220
            $this->isCachable = true;
221
222
            return new EloquentBuilder($query);
223
        }
224
225
        return new CachedBuilder($query);
226
    }
227
228
    public function isCachable() : bool
229
    {
230
        return $this->isCachable
231
            && ! config('laravel-model-caching.disabled');
232
    }
233
234
    public function scopeWithCacheCooldownSeconds(
235
        EloquentBuilder $query,
236
        int $seconds
237
    ) : EloquentBuilder {
238
        $cachePrefix = "genealabs:laravel-model-caching:"
239
            . (config('laravel-model-caching.cache-prefix')
240
                ? config('laravel-model-caching.cache-prefix', '') . ":"
241
                : "");
242
        $modelClassName = get_class($this);
243
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:seconds";
244
245
        $this->cache()
246
            ->rememberForever($cacheKey, function () use ($seconds) {
247
                return $seconds;
248
            });
249
250
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at";
251
        $this->cache()
252
            ->rememberForever($cacheKey, function () {
253
                return now();
254
            });
255
256
        return $query;
257
    }
258
}
259