Skip to content

Commit 5858800

Browse files
committed
Fix invalidation of cache when interacting with belongsToMany
1 parent ef52b7f commit 5858800

File tree

6 files changed

+278
-238
lines changed

6 files changed

+278
-238
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"illuminate/console": "5.5 - 5.6",
1515
"illuminate/database": "5.5 - 5.6",
1616
"illuminate/support": "5.5 - 5.6",
17-
"php": ">=7.1.3"
17+
"php": ">=7.1.3",
18+
"fico7489/laravel-pivot": "^2.2"
1819
},
1920
"require-dev": {
2021
"codedungeon/phpunit-result-printer": "*",

src/CachedBuilder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
<?php namespace GeneaLabs\LaravelModelCaching;
22

3-
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
3+
use GeneaLabs\LaravelModelCaching\Traits\Caching;
44
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
55

66
/**
77
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
88
*/
99
class CachedBuilder extends EloquentBuilder
1010
{
11-
use Cachable;
11+
use Caching;
1212

1313
public function avg($column)
1414
{

src/Traits/Cachable.php

Lines changed: 4 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -1,241 +1,10 @@
11
<?php namespace GeneaLabs\LaravelModelCaching\Traits;
22

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;
3+
use Fico7489\Laravel\Pivot\Traits\PivotEventTrait;
114

125
trait Cachable
136
{
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-
$cache = $cache->tags($tags);
26-
}
27-
28-
return $cache;
29-
}
30-
31-
public function disableCache()
32-
{
33-
$this->isCachable = false;
34-
35-
return $this;
36-
}
37-
38-
public function flushCache(array $tags = [])
39-
{
40-
if (count($tags) === 0) {
41-
$tags = $this->makeCacheTags();
42-
}
43-
44-
$this->cache($tags)->flush();
45-
46-
[$cacheCooldown] = $this->getModelCacheCooldown($this);
47-
48-
if ($cacheCooldown) {
49-
$cachePrefix = $this->getCachePrefix();
50-
$modelClassName = get_class($this);
51-
$cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
52-
53-
$this->cache()
54-
->rememberForever($cacheKey, function () {
55-
return now();
56-
});
57-
}
58-
}
59-
60-
protected function getCachePrefix() : string
61-
{
62-
return "genealabs:laravel-model-caching:"
63-
. (config('laravel-model-caching.cache-prefix')
64-
? config('laravel-model-caching.cache-prefix', '') . ":"
65-
: "");
66-
}
67-
68-
protected function makeCacheKey(
69-
array $columns = ['*'],
70-
$idColumn = null,
71-
string $keyDifferentiator = ''
72-
) : string {
73-
$eagerLoad = $this->eagerLoad ?? [];
74-
$model = $this->model ?? $this;
75-
$query = $this->query ?? app(Builder::class);
76-
77-
return (new CacheKey($eagerLoad, $model, $query))
78-
->make($columns, $idColumn, $keyDifferentiator);
79-
}
80-
81-
protected function makeCacheTags() : array
82-
{
83-
$tags = (new CacheTags($this->eagerLoad ?? [], $this->model ?? $this))
84-
->make();
85-
86-
return $tags;
87-
}
88-
89-
public function getModelCacheCooldown(Model $instance)
90-
{
91-
$cachePrefix = $this->getCachePrefix();
92-
$modelClassName = get_class($instance);
93-
[$cacheCooldown, $invalidatedAt, $savedAt] = $this
94-
->getCacheCooldownDetails($instance, $cachePrefix, $modelClassName);
95-
96-
if (! $cacheCooldown || $cacheCooldown === 0) {
97-
return [null, null, null];
98-
}
99-
100-
return [
101-
$cacheCooldown,
102-
$invalidatedAt,
103-
$savedAt,
104-
];
105-
}
106-
107-
protected function getCacheCooldownDetails(
108-
Model $instance,
109-
string $cachePrefix,
110-
string $modelClassName
111-
) : array {
112-
return [
113-
$instance
114-
->cache()
115-
->get("{$cachePrefix}:{$modelClassName}-cooldown:seconds"),
116-
$instance
117-
->cache()
118-
->get("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at"),
119-
$instance
120-
->cache()
121-
->get("{$cachePrefix}:{$modelClassName}-cooldown:saved-at"),
122-
];
123-
}
124-
125-
protected function checkCooldownAndRemoveIfExpired(Model $instance)
126-
{
127-
[$cacheCooldown, $invalidatedAt] = $this->getModelCacheCooldown($instance);
128-
129-
if (! $cacheCooldown
130-
|| now()->diffInSeconds($invalidatedAt) < $cacheCooldown
131-
) {
132-
return;
133-
}
134-
135-
$cachePrefix = $this->getCachePrefix();
136-
$modelClassName = get_class($instance);
137-
138-
$instance
139-
->cache()
140-
->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
141-
$instance
142-
->cache()
143-
->forget("{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at");
144-
$instance
145-
->cache()
146-
->forget("{$cachePrefix}:{$modelClassName}-cooldown:saved-at");
147-
$instance->flushCache();
148-
}
149-
150-
protected function checkCooldownAndFlushAfterPersiting(Model $instance)
151-
{
152-
[$cacheCooldown, $invalidatedAt] = $instance->getModelCacheCooldown($instance);
153-
154-
if (! $cacheCooldown) {
155-
$instance->flushCache();
156-
157-
return;
158-
}
159-
160-
$this->setCacheCooldownSavedAtTimestamp($instance);
161-
162-
if (now()->diffInSeconds($invalidatedAt) >= $cacheCooldown) {
163-
$instance->flushCache();
164-
}
165-
}
166-
167-
protected function setCacheCooldownSavedAtTimestamp(Model $instance)
168-
{
169-
$cachePrefix = $this->getCachePrefix();
170-
$modelClassName = get_class($instance);
171-
$cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:saved-at";
172-
173-
$instance->cache()
174-
->rememberForever($cacheKey, function () {
175-
return now();
176-
});
177-
}
178-
179-
public static function bootCachable()
180-
{
181-
static::saved(function ($instance) {
182-
$instance->checkCooldownAndFlushAfterPersiting($instance);
183-
});
184-
}
185-
186-
public static function all($columns = ['*'])
187-
{
188-
if (config('laravel-model-caching.disabled')) {
189-
return parent::all($columns);
190-
}
191-
192-
$class = get_called_class();
193-
$instance = new $class;
194-
$tags = [str_slug(get_called_class())];
195-
$key = $instance->makeCacheKey();
196-
197-
return $instance->cache($tags)
198-
->rememberForever($key, function () use ($columns) {
199-
return parent::all($columns);
200-
});
201-
}
202-
203-
public function newEloquentBuilder($query)
204-
{
205-
if (! $this->isCachable()) {
206-
$this->isCachable = true;
207-
208-
return new EloquentBuilder($query);
209-
}
210-
211-
return new CachedBuilder($query);
212-
}
213-
214-
public function isCachable() : bool
215-
{
216-
return $this->isCachable
217-
&& ! config('laravel-model-caching.disabled');
218-
}
219-
220-
public function scopeWithCacheCooldownSeconds(
221-
EloquentBuilder $query,
222-
int $seconds
223-
) : EloquentBuilder {
224-
$cachePrefix = $this->getCachePrefix();
225-
$modelClassName = get_class($this);
226-
$cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:seconds";
227-
228-
$this->cache()
229-
->rememberForever($cacheKey, function () use ($seconds) {
230-
return $seconds;
231-
});
232-
233-
$cacheKey = "{$cachePrefix}:{$modelClassName}-cooldown:invalidated-at";
234-
$this->cache()
235-
->rememberForever($cacheKey, function () {
236-
return now();
237-
});
238-
239-
return $query;
240-
}
7+
use PivotEventTrait;
8+
use Caching;
9+
use ModelCaching;
24110
}

0 commit comments

Comments
 (0)