twig/cache-extra
Twig extension integrating Symfony Cache to cache template fragments. Adds a single cache tag for easy fragment caching in Twig views, improving performance with configurable cache backends via the Symfony Cache component.
Install Dependencies:
composer require twig/cache-extra symfony/cache symfony/cache-common
twig/cache-extra: Core package.symfony/cache: Required for CacheInterface.symfony/cache-common: Common cache utilities (e.g., adapters).Configure Symfony Cache Adapter:
In config/cache.php, extend Laravel’s cache config to include Symfony adapters:
'adapters' => [
'symfony_redis' => [
'driver' => 'redis',
'connection' => 'cache',
'adapter' => \Symfony\Component\Cache\Adapter\RedisAdapter::class,
],
],
Register Twig Extension:
In AppServiceProvider::boot():
$twig = $this->app['view']->getEngine();
$twig->addExtension(new \Twig\Extension\CacheExtension(
$this->app->make(\Symfony\Component\Cache\CacheInterface::class)
));
First Cache Usage:
In a Twig template (e.g., resources/views/partials/header.twig):
{% cache 'header_fragment_v1' %}
<header>
{{ include('nav') }}
{{ include('promo_banner') }}
</header>
{% endcache %}
storage/framework/cache/data/ for generated files.Verify Cache Hit:
php artisan cache:clear.// config/cache.php
'adapters' => [
'shared' => [
'driver' => 'redis',
'connection' => 'cache',
'adapter' => \Symfony\Component\Cache\Adapter\RedisAdapter::class,
'laravel_alias' => 'symfony_redis', // Link to Laravel's cache
],
],
$this->app->singleton(\Symfony\Component\Cache\CacheInterface::class, function ($app) {
return new \Symfony\Component\Cache\Adapter\RedisAdapter(
$app['redis']->connection('cache')
);
});
CacheTagsPruned event to invalidate Symfony Cache:
use Symfony\Component\Cache\CacheItem;
Cache::tags(['products'])->flush();
// Invalidate Symfony Cache for tagged items:
$symfonyCache = app(\Symfony\Component\Cache\CacheInterface::class);
$symfonyCache->deleteItem('products:*'); // Wildcard or specific keys.
{% cache 'user_feed_' ~ user.id ~ '_' ~ current_page %}
{{ render_feed(user, current_page) }}
{% endcache %}
{% if is_logged_in %}
{% cache 'user_prefs_' ~ user.id %}
{{ render_preferences() }}
{% endcache %}
{% endif %}
@cache for hybrid caching:
{% cache 'sidebar' %}
@cache(['key' => 'sidebar_items', 'tags' => ['sidebar']])
{{ render_sidebar_items() }}
@endcache
{% endcache %}
{% cache 'homepage_hero' with {'ttl' => 3600} %}
{{ render_hero() }}
{% endcache %}
// app/Console/Commands/WarmCache.php
use Symfony\Component\Cache\CacheItem;
public function handle()
{
$cache = app(\Symfony\Component\Cache\CacheInterface::class);
$cache->get('homepage_header', function (CacheItem $item) {
$item->expiresAfter(3600);
return view('partials.header')->render();
});
}
$schedule->command('cache:warm')->dailyAt('2:00');
{% cache 'product_' ~ product.id ~ '_v' ~ config('app.cache_version') %}
{{ render_product(product) }}
{% endcache %}
{% cache 'fallback_example' %}
{{ render_expensive_operation() }}
{% else %}
{{ include('fallback_content') }}
{% endcache %}
@cache with Twig syntax:
| Blade | Twig |
|---|---|
@cache(['key' => 'x']) ... @endcache |
{% cache 'x' %} ... {% endcache %} |
@cache(['tags' => ['y']]) ... @endcache |
{% cache 'y:*' %} ... {% endcache %} |
// config/app.php
'aliases' => [
'SymfonyCache' => \Symfony\Component\Cache\CacheInterface::class,
],
{% cache 'key' %}
{{ SymfonyCache.get('key', function() { return 'fallback'; }) }}
{% endcache %}
$cache = Mockery::mock(\Symfony\Component\Cache\CacheInterface::class);
$cache->shouldReceive('get')
->once()
->andReturnUsing(function ($key, $callback) {
return $callback(new CacheItem());
});
$this->app->instance(\Symfony\Component\Cache\CacheInterface::class, $cache);
Key Collisions:
'header' vs. 'header_v1') cause cache stampedes.'header_v1_' ~ locale).Symfony Cache vs. Laravel Cache Conflicts:
file/database drivers.TTL Misconfiguration:
{% cache 'key' with {'ttl' => 300} %} ... {% endcache %}
Blade Template Cache Interference:
bootstrap/cache/) may conflict with Twig cache.// app/Console/Kernel.php
protected function commands()
{
$this->commands([
\Illuminate\Cache\Console\ClearCacheCommand::class,
]);
// Override clear command to skip Twig cache:
$this->app['command.cache.clear']->setCachePaths([
storage_path('framework/cache/data'),
// Exclude: storage_path('framework/cache/twig/'),
]);
}
Cache Tag Invalidation Race Conditions:
CacheTagsPruned event may not sync with Symfony Cache.Cache::tags(['products'])->flush();
CacheInvalidationJob::dispatch
How can I help you explore Laravel packages today?