Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cache Extra Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package via Composer:
    composer require twig/cache-extra
    
  2. Register the Twig extension in your Laravel Twig configuration (config/twig.php):
    'extensions' => [
        // ...
        Twig\Extra\Cache\CacheExtension::class,
    ],
    
  3. Configure Symfony Cache (if not already set up):
    • Ensure your Laravel cache driver (e.g., Redis) is properly configured in .env:
      CACHE_DRIVER=redis
      REDIS_HOST=127.0.0.1
      REDIS_PASSWORD=null
      REDIS_PORT=6379
      
    • Publish the Symfony Cache config (if needed):
      php artisan vendor:publish --provider="Symfony\Cache\CacheBundle\CacheBundle" --tag="config"
      

First Use Case

Cache a static-but-expensive fragment (e.g., a product grid or navigation bar) in a Twig template:

{% cache 'product_grid_' ~ category_id, 300 %}
    {% include 'partials/product-grid.html.twig' %}
{% endcache %}
  • Key: 'product_grid_' ~ category_id (unique per category).
  • TTL: 300 seconds (5 minutes).

Verify It Works

  1. Render the template and check Redis:
    redis-cli keys '*'
    
    You should see a key like twig_cache:product_grid_123.
  2. Force a cache miss by clearing it:
    redis-cli DEL twig_cache:product_grid_123
    
  3. Refresh the page to confirm the fragment re-renders.

Implementation Patterns

Workflows

1. Granular Fragment Caching

  • Use Case: Cache independent blocks (e.g., sidebars, footers, dynamic widgets).
  • Pattern:
    {# Template: sidebar.html.twig #}
    {% cache 'sidebar_' ~ user.role, 600 %}
        {% include 'partials/sidebar-' ~ user.role ~ '.html.twig' %}
    {% endcache %}
    
  • Key Design: Include contextual data (e.g., user.role, tenant_id) to avoid stale data.

2. Tag-Based Invalidation

  • Use Case: Invalidate caches when related data changes (e.g., product updates).
  • Pattern:
    • In Twig: Use tags in cache keys:
      {% cache 'products:grid_' ~ category_id ~ ':tags=products', 300 %}
          {% include 'partials/product-grid.html.twig' %}
      {% endcache %}
      
    • In Laravel: Invalidate tags when data changes:
      Cache::tags(['products'])->flush();
      
  • Note: Requires Redis (tag support is Redis-specific).

3. Conditional Caching

  • Use Case: Disable caching in debug mode or for dynamic content.
  • Pattern:
    {% if app.debug %}
        {% include 'partials/product-grid.html.twig' %}
    {% else %}
        {% cache 'product_grid_' ~ category_id, 300 %}
            {% include 'partials/product-grid.html.twig' %}
        {% endcache %}
    {% endif %}
    

4. Context-Aware Caching

  • Use Case: Multi-tenant or multilingual apps.
  • Pattern:
    {% cache 'dashboard_' ~ user.id ~ '_' ~ app.locale, 900 %}
        {% include 'partials/dashboard.html.twig' %}
    {% endcache %}
    

Integration Tips

Laravel-Specific

  1. Use Laravel’s Cache Facade for Invalidation:

    // Invalidate a specific cache key
    Cache::forget('product_grid_123');
    
    // Invalidate by tag (Redis only)
    Cache::tags(['products'])->flush();
    
  2. Leverage Laravel’s Redis Config:

    • Ensure your config/cache.php uses the redis driver:
      'default' => env('CACHE_DRIVER', 'redis'),
      'stores' => [
          'redis' => [
              'driver' => 'redis',
              'connection' => 'cache',
          ],
      ],
      
  3. Cache Warming:

    • Pre-load critical fragments during low-traffic periods:
      use Symfony\Component\Cache\Adapter\RedisAdapter;
      
      $cache = new RedisAdapter(
          Redis::connection('cache')->getClient(),
          'twig_cache',
          0 // Default lifetime
      );
      $cache->get('product_grid_123', function() {
          // Expensive operation to generate the fragment
          return renderFragment('product-grid');
      });
      

Performance Optimization

  1. Short TTLs for Dynamic Content:

    • Use shorter TTLs (e.g., 60 seconds) for fragments that change frequently.
  2. Avoid Over-Caching:

    • Cache only expensive-to-render fragments. Over-caching can lead to stale data or increased memory usage.
  3. Monitor Cache Hit/Miss Ratios:

    • Use Laravel’s logging or Redis tools to track cache effectiveness:
      redis-cli --scan --pattern 'twig_cache:*'
      

Gotchas and Tips

Pitfalls

  1. Redis Dependency for Tags:

    • Issue: Tag-based invalidation (Cache::tags()->flush()) only works with Redis. File/database drivers ignore tags.
    • Fix: Use Cache::forget() for non-Redis setups or document the limitation in an ADR.
  2. Key Collisions:

    • Issue: Poorly designed keys (e.g., static strings) can cause stale data or memory bloat.
    • Fix: Always include contextual data in keys:
      {# Bad: Static key #}
      {% cache 'sidebar', 300 %} ... {% endcache %}
      
      {# Good: Context-aware #}
      {% cache 'sidebar_' ~ user.role, 300 %} ... {% endcache %}
      
  3. Debugging Cached Fragments:

    • Issue: Cached fragments hide template errors in production.
    • Fix: Use Twig’s debug mode (APP_DEBUG=true) to bypass caching during development.
  4. Cache Stampedes:

    • Issue: High traffic on a cache miss can overload your backend (e.g., 1000 requests regenerating the same fragment).
    • Fix:
      • Use shorter TTLs for high-traffic fragments.
      • Implement cache warming for critical paths.
      • Consider locking mechanisms (e.g., symfony/cache-lock) for shared caches.
  5. Symfony Cache Overhead:

    • Issue: The package adds Symfony dependencies, which may conflict with existing Symfony components.
    • Fix:
      • Audit dependencies with composer why symfony/cache.
      • Use Laravel’s optimize command to reduce autoload overhead:
        php artisan optimize
        

Debugging Tips

  1. Inspect Cache Keys:

    • List all Twig cache keys in Redis:
      redis-cli keys 'twig_cache:*'
      
    • Delete a specific key to test:
      redis-cli DEL twig_cache:product_grid_123
      
  2. Enable Twig Debug Mode:

    • Temporarily disable caching in development:
      {% if app.debug %}
          {% set _cache = false %}
      {% endif %}
      
  3. Log Cache Hits/Misses:

    • Extend the CacheExtension to log statistics:
      use Twig\Extension\AbstractExtension;
      use Psr\Log\LoggerInterface;
      
      class CacheStatsExtension extends AbstractExtension
      {
          public function __construct(private LoggerInterface $logger) {}
      
          public function getTokenParsers(): array
          {
              return [
                  new \Twig\TokenParser\CacheTokenParser($this->logger),
              ];
          }
      }
      

Extension Points

  1. Custom Cache Adapter:

    • Replace the default Symfony adapter with a Laravel-specific one:
      use Symfony\Component\Cache\Adapter\RedisAdapter;
      use Illuminate\Support\Facades\Redis;
      
      $cache = new RedisAdapter(
          Redis::connection('cache')->getClient(),
          'twig_cache',
          0
      );
      
  2. Dynamic TTLs:

    • Override TTL logic based on business rules:
      {% set ttl = user.is_premium ? 3600 : 300 %}
      {% cache 'dashboard_' ~ user.id, ttl %}
          ...
      {% endcache %}
      
  3. Event-Based Invalidation:

    • Listen to Laravel events (e.g., ProductUpdated) to invalidate caches:
      use Illuminate\Support\Facades\Cache;
      
      event(new ProductUpdated
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport