diff --git a/README.md b/README.md index 55436f9..5733c07 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,37 @@ $alice = Kid::whereName('Alice')->cacheFor(60)->cacheTags(['kids'])->first(); $bob = Kid::whereName('Bob')->cacheFor(60)->cacheTags(['kids'])->first(); ``` +### Global Cache Invalidation + +To invalidate all the cache for a specific model, use the `flushQueryCache` method without passing the tags. + +The package automatically appends a list of tags, called **base tags** on each query coming from a model. It defaults to the full model class name. + +In case you want to change the base tags, you can do so in your model. + +```php +class Kid extends Model +{ + use QueryCacheable; + + /** + * Set the base cache tags that will be present + * on all queries. + * + * @return array + */ + protected function getCacheBaseTags(): array + { + return [ + 'custom_tag', + ]; + } +} + +// Automatically works with `custom_tag` +Kid::flushQueryCache(); +``` + ## Relationship Caching Relationships are just another queries. They can be intercepted and modified before the database is hit with the query. The following example needs the `Order` model (or the model associated with the `orders` relationship) to include the `QueryCacheable` trait. diff --git a/database/factories/BookFactory.php b/database/factories/BookFactory.php new file mode 100644 index 0000000..ad0c0f4 --- /dev/null +++ b/database/factories/BookFactory.php @@ -0,0 +1,19 @@ +define(\Rennokki\QueryCache\Test\Models\Book::class, function () { + return [ + 'name' => 'Book'.Str::random(5), + ]; +}); diff --git a/src/Traits/QueryCacheModule.php b/src/Traits/QueryCacheModule.php index 533575a..06b3d6d 100644 --- a/src/Traits/QueryCacheModule.php +++ b/src/Traits/QueryCacheModule.php @@ -22,6 +22,14 @@ trait QueryCacheModule */ protected $cacheTags = null; + /** + * The tags for the query cache that + * will be present on all queries. + * + * @var null|array + */ + protected $cacheBaseTags = null; + /** * The cache driver to be used. * @@ -163,6 +171,10 @@ public function flushQueryCache(array $tags = []): bool return false; } + if (! $tags) { + $tags = $this->getCacheBaseTags(); + } + foreach ($tags as $tag) { self::flushQueryCacheWithTag($tag); } @@ -272,6 +284,20 @@ public function cacheDriver(string $cacheDriver) return $this; } + /** + * Set the base cache tags; the tags + * that will be present on all cached queries. + * + * @param array $tags + * @return \Rennokki\QueryCache\Query\Builder + */ + public function cacheBaseTags(array $tags = []) + { + $this->cacheBaseTags = $tags; + + return $this; + } + /** * Use a plain key instead of a hashed one in the cache driver. * @@ -302,7 +328,11 @@ public function getCacheDriver() public function getCache() { $cache = $this->getCacheDriver(); - $tags = $this->getCacheTags(); + + $tags = array_merge( + $this->getCacheTags() ?: [], + $this->getCacheBaseTags() ?: [] + ); return $tags ? $cache->tags($tags) : $cache; } @@ -348,6 +378,16 @@ public function getCacheTags() return $this->cacheTags; } + /** + * Get the base cache tags attribute. + * + * @return array|null + */ + public function getCacheBaseTags() + { + return $this->cacheBaseTags; + } + /** * Get the cache prefix attribute. * diff --git a/src/Traits/QueryCacheable.php b/src/Traits/QueryCacheable.php index 2ef4fdd..1b6dff5 100644 --- a/src/Traits/QueryCacheable.php +++ b/src/Traits/QueryCacheable.php @@ -41,6 +41,20 @@ protected function newBaseQueryBuilder() $builder->withPlainKey(); } - return $builder; + return $builder + ->cacheBaseTags($this->getCacheBaseTags()); + } + + /** + * Set the base cache tags that will be present + * on all queries. + * + * @return array + */ + protected function getCacheBaseTags(): array + { + return [ + (string) self::class, + ]; } } diff --git a/tests/MethodsTest.php b/tests/MethodsTest.php index 56ed482..ce59477 100644 --- a/tests/MethodsTest.php +++ b/tests/MethodsTest.php @@ -3,6 +3,7 @@ namespace Rennokki\QueryCache\Test; use Cache; +use Rennokki\QueryCache\Test\Models\Book; use Rennokki\QueryCache\Test\Models\Kid; use Rennokki\QueryCache\Test\Models\Post; @@ -89,6 +90,21 @@ public function test_cache_flush_with_more_tags() $this->assertNull($cache); } + public function test_cache_flush_with_default_tags_attached() + { + $book = factory(Book::class)->create(); + $storedBook = Book::cacheFor(now()->addHours(1))->cacheTags(['test'])->first(); + + $cache = Cache::tags(['test', Book::getCacheBaseTags()[0]])->get('leqc:sqlitegetselect * from "books" limit 1a:0:{}'); + $this->assertNotNull($cache); + + Book::flushQueryCache(); + + $cache = Cache::tags(['test', Book::getCacheBaseTags()[0]])->get('leqc:sqlitegetselect * from "books" limit 1a:0:{}'); + + $this->assertNull($cache); + } + public function test_hashed_key() { $kid = factory(Kid::class)->create(); diff --git a/tests/Models/Book.php b/tests/Models/Book.php new file mode 100644 index 0000000..3b923f2 --- /dev/null +++ b/tests/Models/Book.php @@ -0,0 +1,17 @@ +set('auth.providers.users.model', User::class); $app['config']->set('auth.providers.posts.model', Post::class); $app['config']->set('auth.providers.kids.model', Kid::class); + $app['config']->set('auth.providers.books.model', Book::class); $app['config']->set('app.key', 'wslxrEFGWY6GfGhvN9L3wH3KSRJQQpBD'); } diff --git a/tests/database/migrations/2018_07_14_183253_books.php b/tests/database/migrations/2018_07_14_183253_books.php new file mode 100644 index 0000000..b1e9029 --- /dev/null +++ b/tests/database/migrations/2018_07_14_183253_books.php @@ -0,0 +1,32 @@ +increments('id'); + $table->string('name'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('books'); + } +}