elipzis/laravel-cacheable-model
Installation:
composer require elipzis/laravel-cacheable-model
Publish the config file (optional):
php artisan vendor:publish --tag="cacheable-model-config"
Enable Caching for a Model:
Add the Cacheable trait to your Eloquent model:
use ElipZis\Cacheable\Models\Traits\Cacheable;
class Product extends Model
{
use Cacheable;
}
First Use Case: Immediately start querying the model—caching works automatically:
$products = Product::query()->where('category', 'electronics')->get();
// Subsequent identical queries will return cached results
300 seconds (5 minutes). Override per model via getCacheableProperties().'cacheable'. Customize to avoid key collisions.Query Execution:
The package intercepts all QueryBuilder calls (e.g., get(), first(), find()) and checks for cached results.
// Cached if identical query exists
Product::where('price', '>', 100)->get();
Cache Invalidation: Automatically flushes cache on:
create()update()delete()flushCache() calls:
Product::query()->flushCache();
Dynamic Configuration: Override defaults per model:
public function getCacheableProperties(): array
{
return [
'ttl' => 600, // 10-minute cache
'prefix' => 'products',
];
}
Bypass Caching:
Use withoutCache() for uncached queries (e.g., admin dashboards):
Product::query()->withoutCache()->get();
Tagged Cache:
Leverage Laravel’s cache tags for bulk invalidation (requires CacheableQueryBuilder v0.5.0+):
// Tag queries with 'products:electronics'
Product::where('category', 'electronics')->get();
// Flush all tagged entries
Cache::tags('products:electronics')->flush();
Complex Queries:
Supports whereIn, orWhere, join, and raw expressions. Test edge cases like whereNull:
Product::whereNull('deleted_at')->get(); // Works in v0.6.0+
'logging' => [
'enabled' => true,
'channel' => 'cache',
'level' => 'debug',
],
CacheableQueryBuilder in unit tests to isolate logic:
$this->partialMock(Product::class, CacheableQueryBuilder::class);
Performance Overhead:
Cache Key Collisions:
identifier (id) may not uniquely represent queries with orderBy or limit.getCacheKey() in your model:
protected function getCacheKey(): string
{
return md5($this->toCacheKeyArray() . serialize($this->query->getQuery()->orders));
}
Stale Data:
flushCache() in observers or listeners:
Product::observe(ProductObserver::class);
// In observer:
Product::query()->flushCache();
Query Builder Limitations:
Cache Misses: Enable logging to identify uncached queries:
'logging' => ['enabled' => true, 'channel' => 'cache'],
Check logs for CacheableQueryBuilder debug entries.
Key Inspection: Dump the cache key to verify uniqueness:
dd(Product::query()->where('price', '>', 100)->getCacheKey());
Tagged Cache Issues: Ensure tags are consistent:
// Avoid:
Product::where('category', 'electronics')->get(); // Tag: 'cacheable:products:where:category,electronics'
// Prefer:
Product::tagged('products:electronics')->get(); // Explicit tag
Custom Cache Stores:
Bind a custom store in CacheableServiceProvider:
$this->app->bind('cache.store', function () {
return Cache::store('redis')->getStore();
});
Event Hooks:
Listen for cache events (e.g., CacheableCached, CacheableMissed) via Laravel events:
Event::listen(CacheableCached::class, function ($event) {
Log::debug("Cached query: {$event->query}");
});
Query Whitelisting:
Exclude specific queries from caching by overriding shouldCache():
protected function shouldCache(): bool
{
return !str_contains($this->toCacheKeyArray(), 'admin_');
}
ttl => 60 for stock levels).'prefix' => 'tenant1_products',
cache) for easier filtering.Product::query()->where('featured', true)->get(); // Populates cache
Cache::remember() for fallback logic:
$products = Cache::remember('featured_products', 300, function () {
return Product::where('featured', true)->get();
});
Product::with(['images' => function ($query) {
$query->cached(true); // Hypothetical; use package features instead
}])->get();
How can I help you explore Laravel packages today?