Passed
Pull Request — master (#223)
by
unknown
05:06
created

Caching::getAllTraitsUsedByClass()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 2
dl 0
loc 18
rs 10
c 0
b 0
f 0
1
<?php namespace GeneaLabs\LaravelModelCaching\Traits;
2
3
use Carbon\Carbon;
4
use GeneaLabs\LaravelModelCaching\CacheKey;
5
use GeneaLabs\LaravelModelCaching\CacheTags;
6
use Illuminate\Cache\TaggableStore;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Database\Query\Builder;
9
use Illuminate\Support\Collection;
10
11
trait Caching
12
{
13
    protected $isCachable = true;
14
15
    public function cache(array $tags = [])
16
    {
17
        $cache = app('cache');
18
19
        if (config('laravel-model-caching.store')) {
20
            $cache = $cache->store(config('laravel-model-caching.store'));
21
        }
22
23
        if (is_subclass_of($cache->getStore(), TaggableStore::class)) {
24
            $cache = $cache->tags($tags);
25
        }
26
27
        return $cache;
28
    }
29
30
    public function disableModelCaching()
31
    {
32
        $this->isCachable = false;
33
34
        return $this;
35
    }
36
37
    public function flushCache(array $tags = [])
38
    {
39
        if (count($tags) === 0) {
40
            $tags = $this->makeCacheTags();
41
        }
42
43
        $this->cache($tags)->flush();
44
45
        [$cacheCooldown] = $this->getModelCacheCooldown($this);
1 ignored issue
show
Bug introduced by
$this of type GeneaLabs\LaravelModelCaching\Traits\Caching 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

45
        [$cacheCooldown] = $this->getModelCacheCooldown(/** @scrutinizer ignore-type */ $this);
Loading history...
46
47
        if ($cacheCooldown) {
48
            $cachePrefix = $this->getCachePrefix();
49
            $modelClassName = get_class($this);
50
            $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
51
52
            $this->cache()
53
                ->rememberForever($cacheKey, function () {
54
                    return (new Carbon)->now();
55
                });
56
        }
57
    }
58
59
    protected function getCachePrefix() : string
60
    {
61
        return "genealabs:laravel-model-caching:"
62
            . (config('laravel-model-caching.cache-prefix')
63
                ? config('laravel-model-caching.cache-prefix', '') . ":"
64
                : "");
65
    }
66
67
    protected function makeCacheKey(
68
        array $columns = ['*'],
69
        $idColumn = null,
70
        string $keyDifferentiator = ''
71
    ) : string {
72
        $eagerLoad = $this->eagerLoad ?? [];
73
        $model = $this->model ?? $this;
74
        $query = $this->query ?? app('db')->query();
75
76
        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\Caching; 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

76
        return (new CacheKey($eagerLoad, /** @scrutinizer ignore-type */ $model, $query))
Loading history...
77
            ->make($columns, $idColumn, $keyDifferentiator);
78
    }
79
80
    protected function makeCacheTags() : array
81
    {
82
        $eagerLoad = $this->eagerLoad ?? [];
83
        $model = $this->model instanceof Model
84
            ? $this->model
85
            : $this;
86
        $query = $this->query instanceof Builder
87
            ? $this->query
88
            : app('db')->query();
89
        $tags = (new CacheTags($eagerLoad, $model, $query))
1 ignored issue
show
Bug introduced by
It seems like $model can also be of type GeneaLabs\LaravelModelCaching\Traits\Caching; 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

89
        $tags = (new CacheTags($eagerLoad, /** @scrutinizer ignore-type */ $model, $query))
Loading history...
90
            ->make();
91
92
        return $tags;
93
    }
94
95
    public function getModelCacheCooldown(Model $instance) : array
96
    {
97
        $cachePrefix = $this->getCachePrefix();
98
        $modelClassName = get_class($instance);
99
100
        if( $this->cooldownDisabled($modelClassName) ){
101
        	return [null, null, null];
102
        }
103
104
        [$cacheCooldown, $invalidatedAt, $savedAt] = $this
105
            ->getCacheCooldownDetails($instance, $cachePrefix, $modelClassName);
106
107
        if (! $cacheCooldown || $cacheCooldown === 0) {
2 ignored issues
show
introduced by
The condition $cacheCooldown === 0 is always false.
Loading history...
introduced by
$cacheCooldown is of type Illuminate\Database\Eloquent\Collection, thus it always evaluated to true.
Loading history...
108
            return [null, null, null];
109
        }
110
111
        return [$cacheCooldown, $invalidatedAt, $savedAt];
112
    }
113
114
    protected function getCacheCooldownDetails(
115
        Model $instance,
116
        string $cachePrefix,
117
        string $modelClassName
118
    ) : array {
119
        return [
120
            $instance
121
                ->cache()
122
                ->get("{$cachePrefix}:{$modelClassName}-cooldown:seconds"),
1 ignored issue
show
Bug introduced by
$cachePrefix.':'.$modelC...ame.'-cooldown:seconds' 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

122
                ->get(/** @scrutinizer ignore-type */ "{$cachePrefix}:{$modelClassName}-cooldown:seconds"),
Loading history...
123
            $instance
124
                ->cache()
125
                ->get("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at"),
126
            $instance
127
                ->cache()
128
                ->get("{$cachePrefix}:{$modelClassName}-cooldown:saved-at"),
129
        ];
130
    }
131
132
    protected function checkCooldownAndRemoveIfExpired(Model $instance)
133
    {
134
        [$cacheCooldown, $invalidatedAt] = $this->getModelCacheCooldown($instance);
135
136
        if (! $cacheCooldown
137
            || (new Carbon)->now()->diffInSeconds($invalidatedAt) < $cacheCooldown
138
        ) {
139
            return;
140
        }
141
142
        $cachePrefix = $this->getCachePrefix();
143
        $modelClassName = get_class($instance);
144
145
        $instance
146
            ->cache()
147
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:seconds");
148
        $instance
149
            ->cache()
150
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
151
        $instance
152
            ->cache()
153
            ->forget("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
154
        $instance->flushCache();
155
    }
156
157
    protected function checkCooldownAndFlushAfterPersisting(Model $instance)
158
    {
159
        [$cacheCooldown, $invalidatedAt] = $instance->getModelCacheCooldown($instance);
160
161
        if (! $cacheCooldown) {
162
            $instance->flushCache();
163
164
            return;
165
        }
166
167
        $this->setCacheCooldownSavedAtTimestamp($instance);
168
169
        if ((new Carbon)->now()->diffInSeconds($invalidatedAt) >= $cacheCooldown) {
170
            $instance->flushCache();
171
        }
172
    }
173
174
    public function isCachable() : bool
175
    {
176
        return $this->isCachable
177
            && ! config('laravel-model-caching.disabled');
178
    }
179
180
    public function cooldownDisabled(string $class) : bool
181
    {
182
    	return ! config('laravel-model-caching.enable-cooldown', true)
183
		    || in_array($class, config('laravel-model-caching.cooldown-disable', []))
184
		    || $this->getAllTraitsUsedByClass($class)
185
		            ->contains('GeneaLabs\LaravelModelCaching\Traits\DisableCooldown');
186
    }
187
188
	/** @SuppressWarnings(PHPMD.BooleanArgumentFlag) */
189
	public static function getAllTraitsUsedByClass(
190
		string $classname,
191
		bool $autoload = true
192
	) : Collection {
193
		$traits = collect();
194
195
		if (class_exists($classname, $autoload)) {
196
			$traits = collect(class_uses($classname, $autoload));
197
		}
198
199
		$parentClass = get_parent_class($classname);
200
201
		if ($parentClass) {
202
			$traits = $traits
203
				->merge(static::getAllTraitsUsedByClass($parentClass, $autoload));
204
		}
205
206
		return $traits;
207
	}
208
209
    protected function setCacheCooldownSavedAtTimestamp(Model $instance)
210
    {
211
        $cachePrefix = $this->getCachePrefix();
212
        $modelClassName = get_class($instance);
213
        $cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
214
215
        $instance->cache()
216
            ->rememberForever($cacheKey, function () {
217
                return (new Carbon)->now();
218
            });
219
    }
220
}
221