craftcms/laravel-dependency-aware-cache
Laravel cache layer inspired by Craft CMS that tracks dependencies between cached items. Invalidate related keys automatically when a dependency changes, reducing manual cache busting and keeping derived data consistent across your app.
Installation:
composer require craftcms/laravel-dependency-aware-cache
Register the service provider in config/app.php under providers:
Craftcms\DependencyAwareCache\DependencyAwareCacheServiceProvider::class,
Basic Usage:
Replace Laravel's default cache repository in config/cache.php:
'default' => [
'driver' => \Craftcms\DependencyAwareCache\DependencyAwareCache::class,
'key_prefix' => env('CACHE_PREFIX', 'laravel_'),
// ... other config (e.g., 'store' => 'file', 'path' => storage_path('framework/cache'))
],
First Use Case: Cache data with dependencies (e.g., a user's profile) that invalidates when related models change:
use Craftcms\DependencyAwareCache\DependencyAwareCache;
$cache = app(DependencyAwareCache::class);
// Cache with dependencies
$cache->put('user:123:profile', $profileData, now()->addMinutes(60), [
'dependencies' => ['user:123'], // Invalidate when user:123 changes
]);
// Retrieve cached data
$profile = $cache->get('user:123:profile');
Model-Based Dependencies: Use Eloquent model IDs or UUIDs as dependency keys to auto-invalidate cache when models are updated:
$cache->put('product:'.$product->id, $productData, now()->addHours(1), [
'dependencies' => ['product:'.$product->id],
]);
Event-Driven Invalidation:
Listen to model events (e.g., updated, deleted) to trigger cache invalidation:
use Illuminate\Database\Eloquent\Model;
Model::updated(function ($model) {
app(DependencyAwareCache::class)->forgetByDependencies(['model:'.$model->getKey()]);
});
Composite Dependencies: Combine multiple dependencies (e.g., user + settings) for granular invalidation:
$cache->put('dashboard:user:'.$user->id, $dashboardData, now()->addDays(1), [
'dependencies' => [
'user:'.$user->id,
'settings:theme',
],
]);
Cache Tags: Use dependency keys as "tags" for bulk invalidation (e.g., clear all user-related cache):
$cache->forgetByDependencies(['user:*']); // Wildcard support
Fallback to Default Cache: Chain with Laravel’s default cache for non-dependency-aware operations:
$defaultCache = cache();
$dependencyCache = app(DependencyAwareCache::class);
if ($dependencyCache->has('key')) {
return $dependencyCache->get('key');
}
return $defaultCache->remember('key', $ttl, fn() => $expensiveOperation());
Testing: Mock dependencies in tests to verify invalidation logic:
$this->mock(DependencyAwareCache::class)
->shouldReceive('forgetByDependencies')
->once()
->with(['user:1']);
Dependency Key Collisions:
Avoid ambiguous keys (e.g., user:1 vs. user:12). Use UUIDs or qualified namespaces:
// Bad: 'user:1' (ambiguous)
// Good: 'users:1' or 'user:uuid-here'
Circular Dependencies:
Ensure dependencies don’t create loops (e.g., A depends on B, B depends on A). Use a DAG (Directed Acyclic Graph) structure.
Wildcard Overuse:
Wildcards (e.g., user:*) can lead to unintended mass invalidations. Use sparingly or scope with prefixes:
// Safe: 'users:active:*'
// Risky: 'users:*'
Memory Leaks:
Unbounded dependencies (e.g., caching all post:* tags) can bloat the dependency tracker. Set TTLs or use forgetByDependencies explicitly.
Dependency Tracker: Inspect tracked dependencies via:
$cache->getDependencyTracker()->getAllDependencies();
Useful for diagnosing missing invalidations.
Log Invalidation: Enable debug logging for invalidation events:
config([
'dependency_aware_cache' => [
'debug' => true,
],
]);
Custom Dependency Resolvers:
Extend the dependency system by implementing Craftcms\DependencyAwareCache\Contracts\DependencyResolver:
class ModelDependencyResolver implements DependencyResolver {
public function resolve(string $dependency): array {
return ['model:'.$dependency]; // Transform '123' to 'model:123'
}
}
Register in config/dependency_aware_cache.php:
'resolvers' => [
\App\Resolvers\ModelDependencyResolver::class,
],
Event Listeners:
Subscribe to DependencyAwareCacheInvalidated events to react to invalidations:
use Craftcms\DependencyAwareCache\Events\DependencyAwareCacheInvalidated;
event(new DependencyAwareCacheInvalidated($dependencies, $cacheKey));
Store-Specific Config:
Override per-store settings (e.g., disable dependencies for redis store):
'stores' => [
'redis' => [
'driver' => 'redis',
'dependencies_enabled' => false,
],
],
How can I help you explore Laravel packages today?