twig/cache-extra
Twig Cache Extension integrates the Symfony Cache component into Twig, adding a single cache tag that lets you cache template fragments directly in your templates for faster rendering.
Verdict up front: do not adopt this for a Laravel/Blade application. twig/cache-extra is a 40-star, zero-dependent, single-tag extension for the Twig templating engine. It solves a real problem (fragment caching inside Twig templates), but Laravel's default view layer is Blade, and Laravel 11+ ships a native @cache Blade directive that covers the same use case with no extra dependency. The only defensible adoption path is an application that already renders Twig templates (legacy Symfony component, a microservice with a Twig layer, or a deliberate Twig-on-Laravel integration) and wants to avoid hand-rolling fragment caching.
{% cache %} tag around template fragments. Architecturally that is the right place for fragment caching, but it assumes Twig — not Blade, not Livewire, not Inertia.Symfony\Contracts\Cache\CacheInterface. Laravel has its own Illuminate\Contracts\Cache\Repository tree. A Laravel app adopting this ends up with two cache stacks unless a bridge is installed, which is where most of the integration cost and risk lives.Symfony\Component\Cache\Psr16Cache, which wraps a PSR-16 store into the Symfony contract. So new Psr16Cache(Cache::store('redis')) is the realistic adapter. That bridge deliberately drops Symfony-side features (native stampede protection via locks, PSR-6 item metadata) and gives the package a narrower capability set than it has on Symfony.@cache." That is negative value. Reject.{% cache %} tag does not take a tags=[...] argument, TTL is expressed as ttl(seconds) rather than ttl=60, and Laravel's taggable stores expose flush() on a tag set rather than invalidateTags(). Do not let engineering estimate off of those snippets — they will produce a wrong design.| Risk | Severity | Why it matters | Mitigation |
|---|---|---|---|
| Dual cache stacks (Symfony vs Illuminate) | High | Two TTL semantics, two invalidation paths, two sets of keys to purge on deploy | Isolate to one store; namespace-prefix all Twig fragment keys; single purge command in deploy |
| Adapter bridge is narrow | High | Through PSR-16 you lose Symfony locking/stampede protection; heavy traffic on a cold key will dogpile | Accept, or wrap the fragment body in Cache::remember() for the expensive part |
| Invalidation correctness | High | Twig tag has no tag-based invalidation; you are on key-only expiry | Key design with domain + id + locale + version segment; version bump to invalidate en masse |
| Key explosion / collision | Medium | Poorly constructed keys silently serve wrong user's content — a security-adjacent defect | Mandate key conventions in review; include user/tenant scope wherever output is personalized |
| Templating duplication | Medium | Maintaining Twig templates inside a Blade application splits ownership and tooling | Only justified if Twig already exists |
| Upstream drift | Low | Stable, low-activity package, but a Twig major could change the extension contract | Pin the version; it is not on a critical path if @cache is the fallback |
@cache.@cache cannot? If the answer is "nothing", we are buying a dependency for syntax preference.Cache facade. Native @cache is the idiomatic answer; composer require twig/cache-extra adds a templating engine dependency to solve a solved problem.@cache.twig/cache-extra + symfony/cache, bind Symfony\Contracts\Cache\CacheInterface in a service provider via new Psr16Cache(Cache::store('redis')), register the extension on the Twig environment, and cache a header/footer partial. Measure hit rate and response-time delta.twig_frag:<domain>:<id>:<locale>:<v>; confirm the extension's own key prefixing behavior before assuming it exists. Publish the convention as a review checklist.file and database work but undercut the point. Laravel's taggable stores (Redis/Memcached/DynamoDB) expose different invalidation semantics than Symfony's tag system, so do not design around cross-compatible tags.put()/remember() in some call sites; PSR-16 and the Twig tag use seconds. A unit mismatch here is a silent correctness bug, not an exception.twig/twig and symfony/cache explicitly; this package rides Twig's release train.Cache::remember() so the inner layer absorbs the stampede.user.id or tenant.id are safe (no cross-user leakage) but multiply entries. Budget memory per fragment and cap the high-cardinality ones with short TTLs.| Failure | Symptom | Detection | Response |
|---|---|---|---|
| Extension not registered / adapter unbound | CacheExtension requires a cache or container resolution error at boot |
Fails at deploy, loudly | Config flag; fail-fast at boot rather than at first render |
| Key collision or missing scope | Wrong user's content rendered — data leak | Synthetic test rendering two users against one key; canary checks | Key convention enforced in review; short TTLs on personalized fragments |
| Stale fragments after deploy | Users see pre-deploy markup, broken links to removed assets | Post-deploy smoke test; hit-rate and version-key assertions | Purge command in the deploy pipeline, keyed by a version segment |
| Stampede on cold/expired key | Latency spike, DB/API throttling | p99 latency, DB connection saturation | Jittered TTLs; inner remember() guard; warm critical fragments post-deploy |
| TTL unit confusion | Fragments expire too fast or effectively never | Inspect key TTLs in Redis directly | Standardize on seconds in the adapter config; assert in tests |
| Dual-stack purge drift | cache:clear doesn't clear Twig fragments (or vice versa) |
Purge both, verify both empty | Single deploy command that clears both namespaces explicitly |
| Shared Redis, unnamespaced keys | Purging app cache evicts all fragments | Key inventory audit | Mandatory prefix; separate logical DB if feasible |
How can I help you explore Laravel packages today?