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

Cachable   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 243
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 243
rs 9.2
c 0
b 0
f 0
wmc 34

15 Methods

Rating   Name   Duplication   Size   Complexity  
A addTagsWhenCalledFromCachedBuilder() 0 10 2
A cache() 0 14 3
A makeCacheTags() 0 6 1
A flushCache() 0 21 4
A makeCacheKey() 0 11 1
B getModelCacheCooldown() 0 28 3
A disableCache() 0 5 1
A newEloquentBuilder() 0 9 2
A checkCooldownAndFlushAfterPersiting() 0 16 4
B checkCooldownAndRemoveIfExpired() 0 26 4
A setCacheCooldownSavedAtTimestamp() 0 12 2
A isCachable() 0 4 2
A bootCachable() 0 5 1
A scopeWithCacheCooldownSeconds() 0 23 2
A all() 0 14 2
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);
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);
84
85
        return (new CacheKey($eagerLoad, $model, $query))
86
            ->make($columns, $idColumn, $keyDifferentiator);
87
    }
88
89
    protected function makeCacheTags() : array
90
    {
91
        $tags = (new CacheTags($this->eagerLoad ?? [], $this->model ?? $this))
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");
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
            || now()->diffInSeconds($invalidatedAt) < $cacheCooldown
134
        ) {
135
            return;
136
        }
137
138
        $cachePrefix = "genealabs:laravel-model-caching:"
139
            . (config('laravel-model-caching.cache-prefix')
140
                ? config('laravel-model-caching.cache-prefix', '') . ":"
141
                : "");
142
        $modelClassName = get_class($instance);
143
144
        $instance
145
            ->cache()
146
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
147
        $instance
148
            ->cache()
149
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
150
        $instance
151
            ->cache()
152
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
153
        $instance->flushCache();
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
        $this->setCacheCooldownSavedAtTimestamp($instance);
167
168
        if ($savedAt > $invalidatedAt
169
                && now()->diffInSeconds($invalidatedAt) >= $cacheCooldown
170
        ) {
171
            $instance->flushCache();
172
        }
173
    }
174
175
    protected function setCacheCooldownSavedAtTimestamp(Model $instance)
176
    {
177
        $cachePrefix = "genealabs:laravel-model-caching:"
178
            . (config('laravel-model-caching.cache-prefix')
179
                ? config('laravel-model-caching.cache-prefix', '') . ":"
180
                : "");
181
        $modelClassName = get_class($instance);
182
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
183
184
        $instance->cache()
185
            ->rememberForever($cacheKey, function () {
186
                return now();
187
            });
188
    }
189
190
    public static function bootCachable()
191
    {
192
        // TODO: add for deleted,updated,etc?
193
        static::saved(function ($instance) {
194
            $instance->checkCooldownAndFlushAfterPersiting($instance);
195
        });
196
    }
197
198
    public static function all($columns = ['*'])
199
    {
200
        if (config('laravel-model-caching.disabled')) {
201
            return parent::all($columns);
202
        }
203
204
        $class = get_called_class();
205
        $instance = new $class;
206
        $tags = [str_slug(get_called_class())];
207
        $key = $instance->makeCacheKey();
208
209
        return $instance->cache($tags)
210
            ->rememberForever($key, function () use ($columns) {
211
                return parent::all($columns);
212
            });
213
    }
214
215
    public function newEloquentBuilder($query)
216
    {
217
        if (! $this->isCachable()) {
218
            $this->isCachable = true;
219
220
            return new EloquentBuilder($query);
221
        }
222
223
        return new CachedBuilder($query);
224
    }
225
226
    public function isCachable() : bool
227
    {
228
        return $this->isCachable
229
            && ! config('laravel-model-caching.disabled');
230
    }
231
232
    public function scopeWithCacheCooldownSeconds(
233
        EloquentBuilder $query,
234
        int $seconds
235
    ) : EloquentBuilder {
236
        $cachePrefix = "genealabs:laravel-model-caching:"
237
            . (config('laravel-model-caching.cache-prefix')
238
                ? config('laravel-model-caching.cache-prefix', '') . ":"
239
                : "");
240
        $modelClassName = get_class($this);
241
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:seconds";
242
243
        $this->cache()
244
            ->rememberForever($cacheKey, function () use ($seconds) {
245
                return $seconds;
246
            });
247
248
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at";
249
        $this->cache()
250
            ->rememberForever($cacheKey, function () {
251
                return now();
252
            });
253
254
        return $query;
255
    }
256
}
257