danilovl/cache-response-bundle
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require danilovl/cache-response-bundle
Ensure Danilovl\CacheResponseBundle\CacheResponseBundle::class is enabled in config/bundles.php.
Basic Configuration:
Enable the bundle in config/packages/danilovl_cache_response.yaml:
danilovl_cache_response:
enable: true
First Use Case: Cache a controller response for 60 seconds using a static key:
use Danilovl\CacheResponseBundle\Attribute\CacheResponseAttribute;
#[CacheResponseAttribute(key: 'homepage', expiresAfter: 60)]
public function index(): Response
{
return new Response('Cached content');
}
config/packages/danilovl_cache_response.yaml (Configuration)src/Attribute/CacheResponseAttribute.php (Attribute definition)src/Command/ (CLI commands for cache management)Use __METHOD__ or a combination of route/query parameters for unique keys:
#[CacheResponseAttribute(
key: __METHOD__,
useRoute: true,
useQuery: true,
expiresAfter: 300
)]
public function show(Product $product): Response
{
return $this->render('product/show.html.twig', ['product' => $product]);
}
Disable caching for POST requests or specific queries:
#[CacheResponseAttribute(
key: 'dashboard',
expiresAfter: 900,
disableOnRequest: true, // Skip cache for POST/PUT/PATCH
disableOnQuery: true // Skip cache if query params exist
)]
public function dashboard(): Response
{
return $this->render('dashboard.html.twig');
}
Implement CacheKeyFactoryInterface for complex key logic:
class UserSpecificCacheKeyFactory implements CacheKeyFactoryInterface
{
public function __invoke(Request $request, array $options): string
{
return 'user_'.($request->attributes->get('user')?->getId()).'_'.md5($request->getPathInfo());
}
}
Use it in your controller:
#[CacheResponseAttribute(factory: UserSpecificCacheKeyFactory::class)]
public function profile(): Response
{
// ...
}
Clear caches during critical events (e.g., user updates):
use Danilovl\CacheResponseBundle\Event\ClearCacheResponseKeyEvent;
public function onUserUpdated(UserUpdatedEvent $event)
{
$this->eventDispatcher->dispatch(
new ClearCacheResponseKeyEvent('user_'.$event->getUser()->getId().'_*')
);
}
Adjust kernel listener priorities in config:
danilovl_cache_response:
kernel_controller_priority: -100 # Run before other controllers
kernel_response_priority: 100 # Run after other responses
Service Provider Setup:
Register the bundle in config/app.php under providers:
Danilovl\CacheResponseBundle\CacheResponseBundle::class,
Cache Adapter Configuration:
Use Laravel’s cache drivers (e.g., cache.array, cache.redis) by configuring the cache_adapter:
danilovl_cache_response:
cache_adapter: 'Symfony\Contracts\Cache\CacheInterface'
Bind the adapter in Laravel’s DI container (e.g., AppServiceProvider):
$this->app->bind(
CacheItemPoolInterface::class,
fn() => Cache::store('array')->getCachePool()
);
Artisan Command Aliases:
Create Laravel-friendly aliases for CLI commands in app/Console/Kernel.php:
protected $commands = [
Commands\CacheResponseListCommand::class,
Commands\CacheResponseClearCommand::class,
];
Now use:
php artisan cache:response:list
php artisan cache:response:clear --cacheKey=homepage
Attribute Routing: Combine with Laravel’s route caching:
#[Route('/cached', name: 'cached_route')]
#[CacheResponseAttribute(key: 'cached_route', expiresAfter: 3600)]
public function cachedRoute(): Response
{
return response()->json(['data' => 'cached']);
}
Key Collisions:
useRequest: true with complex requests (e.g., file uploads) may generate overly long keys or collisions.useQuery: true or useRoute: true for most use cases. For custom logic, implement CacheKeyFactoryInterface.Cache Invalidation:
ClearCacheResponseKeyEvent or CLI commands (php artisan cache:response:clear) during critical updates.Priority Conflicts:
kernel_controller_priority and kernel_response_priority set to extreme values (e.g., -1000 or 1000) to isolate issues.Environment-Specific Caching:
useEnv: true may cause cache keys to differ between environments (e.g., APP_DEBUG).useEnv: true unless explicitly needed. Use environment-specific cache prefixes instead.Session Data Bloat:
useSession: true includes session data in keys, which can grow unbounded.List All Cache Keys:
php artisan cache:response:list
Clear Specific Patterns: Use wildcards to clear related keys:
php artisan cache:response:clear --cacheKey="user_*"
Profiler Integration:
Log Cache Hits/Misses: Enable debug mode and check logs for:
CacheResponseBundle: Cache hit/miss for key "..." in "..." seconds.
Default Cache Adapter:
cache_adapter is not set, the bundle falls back to Symfony’s default CacheItemPoolInterface (e.g., cache.app in Laravel).danilovl_cache_response:
cache_adapter: 'Symfony\Contracts\Cache\CacheInterface'
Boolean Flags:
disableOnQuery are strictly boolean. Avoid passing strings or integers.disableOnQuery: 1 # Won't work!
disableOnQuery: true
ExpiresAfter vs. ExpiresAt:
expiresAfter accepts seconds or a DateInterval (e.g., expiresAfter: 'PT1H').expiresAt requires a DateTimeInterface object.expiresAfter for simplicity unless you need precise expiration times.Custom Cache Tags:
Extend the bundle to support cache tags (e.g., CacheTagAwareInterface) for group invalidation:
#[CacheResponseAttribute(tags: ['products'])]
public function productList(): Response
{
// ...
}
CacheResponseAttribute and add tag support to the listener.Cache Warmup: Pre-populate caches during deployment:
use Danilovl\CacheResponseBundle\CacheResponseWarmer;
class CacheWarmer implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$container->addCompilerPass(new CacheResponseWarmer());
}
}
Vary Headers:
Add Vary headers dynamically based on cache keys:
// In a subscriber or listener
$response->headers->set('Vary', 'Accept, User-Agent');
How can I help you explore Laravel packages today?