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

Laravel Flexible Cache Polyfill Laravel Package

spatie/laravel-flexible-cache-polyfill

Polyfill for Laravel 10 that adds Cache::flexible() (introduced in Laravel 11). Implements stale-while-revalidate caching so expired values can be served immediately while recalculation runs in the background, avoiding slow responses.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/laravel-flexible-cache-polyfill
    

    No publisher or configuration required—just add the package to your composer.json.

  2. First Use Case: Replace Cache::remember() with Cache::flexible() for stale-while-revalidate (SWR) behavior in Laravel 10:

    $value = Cache::flexible('key', now()->addMinutes(5), function () {
        return ExpensiveOperation::run();
    });
    
    • Key Difference: Unlike remember, flexible serves stale data immediately if the cache is expired, while revalidating in the background.
  3. Where to Look First:


Implementation Patterns

Core Workflows

  1. Migrating from remember to flexible:

    • Before:
      $data = Cache::remember('expensive_key', 300, fn() => fetchFromDatabase());
      
    • After:
      $data = Cache::flexible('expensive_key', 300, fn() => fetchFromDatabase());
      
    • Impact: Reduces perceived latency during cache misses by serving stale data while refreshing.
  2. Combining with Other Cache Methods:

    • Use flexible alongside put()/get() for hybrid caching:
      Cache::put('fallback_key', $fallbackValue, 60);
      $value = Cache::flexible('primary_key', 300, fn() => generatePrimaryValue());
      return $value ?? Cache::get('fallback_key');
      
  3. Dynamic TTLs:

    • Pass dynamic TTLs (e.g., based on user activity):
      $ttl = Auth::user()->isPremium() ? 3600 : 600;
      $data = Cache::flexible('user_data', $ttl, fn() => fetchUserData());
      
  4. Tag-Based Invalidation:

    • Leverage Laravel’s cache tags (if using file/database driver) to invalidate related keys:
      Cache::flexible('product_123', 300, fn() => getProduct(123), tags: ['products']);
      Cache::tags('products')->flush(); // Invalidates all tagged keys
      

Integration Tips

  • Testing: Use Cache::shouldReceive('flexible')->andReturnUsing(...) in PHPUnit to mock responses:
    Cache::shouldReceive('flexible')
        ->with('key', 300, $callback)
        ->andReturnUsing(fn($key, $ttl, $callback) => $callback());
    
  • Logging: Log cache hits/misses to monitor SWR behavior:
    Cache::flexible('key', 300, fn() => {
        Log::debug('Cache miss for key', ['key' => 'key']);
        return expensiveOperation();
    });
    

Gotchas and Tips

Pitfalls

  1. Driver Limitations:

    • Redis/Memcached: SWR requires background workers (e.g., Laravel Queues) to revalidate stale data. Without them, flexible behaves like remember.
    • File/Database Drivers: No background revalidation—use remember instead for these drivers.
  2. Race Conditions:

    • If multiple requests hit an expired key simultaneously, all may serve stale data until one completes revalidation. Mitigate with:
      • Short TTLs (e.g., 10–30 seconds) for critical data.
      • Explicit Cache::put() after revalidation to force freshness.
  3. Callback Execution:

    • The callback runs only if the cache is stale or missing. Avoid side effects (e.g., logging, analytics) in the callback unless intentional.
  4. Laravel 10 vs. 11:

    • The polyfill mimics Laravel 11’s flexible() but lacks some features (e.g., tags parameter in older Laravel 10). Check the package’s changelog for updates.

Debugging

  • Verify SWR Behavior:
    if (Cache::has('key')) {
        $stale = Cache::get('key');
        $fresh = Cache::flexible('key', 300, fn() => 'fresh');
        dd(['stale' => $stale, 'fresh' => $fresh]); // Should show stale first, then fresh.
    }
    
  • Check Cache Events: Listen for Cache::stale events (if using Laravel 11+ features) or log manually:
    Cache::extend('flexible', fn() => Cache::store('redis')->driver())->extend('flexible', function ($app) {
        return Cache::repository(new FlexibleCacheStore($app['cache.stores.redis']));
    });
    

Extension Points

  1. Custom Stores: Extend the polyfill for non-Redis drivers by implementing Spatie\Cache\FlexibleCacheStore:
    Cache::extend('database', fn() => Cache::repository(new FlexibleDatabaseCacheStore(
        Cache::store('database')->driver()
    )));
    
  2. Middleware: Use middleware to enforce SWR for specific routes:
    public function handle(Request $request, Closure $next) {
        Cache::shouldUseFlexible = true; // Hypothetical; check package for actual API.
        return $next($request);
    }
    
  3. Fallback Logic: Combine with Cache::get() to handle missing keys gracefully:
    $value = Cache::flexible('key', 300, fn() => defaultValue()) ?? Cache::get('fallback_key');
    
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