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 Contracts Laravel Package

symfony/cache-contracts

Symfony Cache Contracts defines lightweight, PSR-friendly interfaces for cache and tag-aware caching, enabling consistent cache usage across Symfony components and third-party libraries. Use it to type-hint against stable APIs while swapping cache implementations.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. No Installation Needed in Laravel: Since Laravel already includes symfony/cache-contracts as a transitive dependency (via symfony/cache), skip composer require. Focus on leveraging Laravel’s built-in Cache facade or dependency-injected interfaces.

  2. First Use Case: Caching a Simple Value

    // In a controller or service
    $value = Cache::get('key');
    if (!$value) {
        $value = expensiveOperation();
        Cache::put('key', $value, now()->addMinutes(10));
    }
    

    Under the hood, Laravel’s Cache facade uses Symfony’s CacheItemPoolInterface (PSR-6).

  3. Where to Look First:


Implementation Patterns

1. Dependency Injection (PSR-6)

Inject Psr\Cache\CacheItemPoolInterface (or Symfony\Contracts\Cache\CacheInterface for simpler use cases) into services for testability and flexibility:

use Psr\Cache\CacheItemPoolInterface;

class MyService {
    public function __construct(private CacheItemPoolInterface $cache) {}

    public function fetchData() {
        $item = $this->cache->getItem('data_key');
        if (!$item->isHit()) {
            $item->set(expensiveFetch());
            $this->cache->save($item);
        }
        return $item->get();
    }
}

Laravel-specific: Use Cache::store('redis')->getItemPool() to access a specific driver’s pool.


2. Tag-Based Invalidation

Leverage TagAwareCacheInterface (via symfony/cache's TagAwareAdapter) to invalidate related cache entries:

// In a service or event listener
Cache::tags(['products'])->flush(); // Clears all items tagged 'products'

Use Case: Invalidate product listings after a bulk update.


3. Fallback Cache Chain

Combine adapters for resilience (e.g., in-memory fallback to Redis):

use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

// In a service constructor
public function __construct(
    AdapterInterface $primaryCache,
    private ArrayAdapter $fallbackCache
) {
    $this->primaryCache = $primaryCache;
}

// Usage
try {
    $item = $this->primaryCache->getItem('key');
    if (!$item->isHit()) {
        throw new \RuntimeException('Cache miss');
    }
} catch (\RuntimeException) {
    $item = $this->fallbackCache->getItem('key');
    // Fallback logic...
}

4. Deferred Cache Operations

Batch operations with defer() and commit() for performance:

use Psr\Cache\CacheItemPoolInterface;

public function __construct(private CacheItemPoolInterface $cache) {}

public function batchSave(array $data) {
    $items = [];
    foreach ($data as $key => $value) {
        $items[$key] = $this->cache->getItem($key);
        $items[$key]->set($value);
    }
    $this->cache->commit(); // Atomic save
}

5. Laravel-Specific Patterns

  • Driver-Specific Configuration:
    // config/cache.php
    'stores' => [
        'redis' => [
            'driver' => 'redis',
            'connection' => 'cache',
            'tags' => ['products', 'users'], // Enable tag support
        ],
    ];
    
  • Cache Middleware: Use Cache::remember() in middleware for route-level caching:
    public function handle($request, Closure $next) {
        return Cache::remember('homepage', now()->addHours(1), function () {
            return $next($request);
        });
    }
    

Gotchas and Tips

1. No Implementation = No Cache

  • Pitfall: Assuming symfony/cache-contracts alone provides caching. It only defines interfaces.
  • Fix: Pair with a driver (e.g., symfony/cache, predis/predis for Redis, or doctrine/cache).
  • Laravel Shortcut: Use Cache::driver('redis')—Laravel handles the adapter setup.

2. Version Mismatch Risks

  • Pitfall: Laravel’s symfony/cache version may not align with your custom adapter’s contract version.
    • Example: Laravel 10 uses symfony/cache-contracts:^2.0, but a custom adapter might target v1.0.
  • Fix: Pin versions in composer.json:
    "require": {
        "symfony/cache-contracts": "^2.0",
        "symfony/cache": "^6.0"
    }
    

3. Serialization Behavior

  • Pitfall: Adapters serialize data differently:
    • FilesystemAdapter: Serializes non-scalar values (e.g., objects) to JSON.
    • ArrayAdapter: Stores data in-memory only (no persistence).
  • Tip: Use Cache::store('array')->getItemPool() for testing, but avoid in production.
  • Workaround: Normalize data to JSON strings before caching:
    $item->set(json_encode($complexObject));
    

4. Debugging Stale Cache

  • Pitfall: ArrayAdapter resets on each request (useful for tests but misleading in dev).
  • Tip: Use Cache::forget('key') or Cache::clear() to test invalidation.
  • Advanced: Implement a custom adapter extending CacheItemPoolDecorator to log misses:
    use Symfony\Component\Cache\Adapter\CacheItemPoolDecorator;
    
    class LoggingCachePool extends CacheItemPoolDecorator {
        public function getItem($key) {
            $item = parent::getItem($key);
            if (!$item->isHit()) {
                Log::debug("Cache miss for key: $key");
            }
            return $item;
        }
    }
    

5. PSR-6 vs PSR-16 Confusion

  • Pitfall: Mixing CacheItemPoolInterface (PSR-6) with CacheInterface (PSR-16).
  • Tip:
    • Use PSR-6 (CacheItemPoolInterface) for advanced features (tags, deferred commits).
    • Use PSR-16 (CacheInterface) for simple key-value caching (e.g., get(), set()).
  • Laravel Note: The Cache facade defaults to PSR-6 under the hood.

6. Extension Points

  • Custom Adapter: Implement CacheItemPoolInterface for a new backend (e.g., DynamoDB):
    use Psr\Cache\CacheItemPoolInterface;
    use Psr\Cache\CacheItemInterface;
    
    class DynamoDbCachePool implements CacheItemPoolInterface {
        public function getItem($key): CacheItemInterface {
            // Fetch from DynamoDB
            return new DynamoDbCacheItem($key, $data);
        }
        // Implement other methods...
    }
    
  • Decorator Pattern: Extend CacheItemPoolDecorator to add logic (e.g., logging, metrics):
    use Symfony\Component\Cache\Adapter\CacheItemPoolDecorator;
    
    class MetricsCachePool extends CacheItemPoolDecorator {
        public function save(CacheItemInterface $item) {
            $this->trackMetric('cache.save');
            parent::save($item);
        }
    }
    

7. Laravel-Specific Quirks

  • Tag Support: Ensure your driver supports tags (e.g., Redis 4.0+ with Cache::tags()).
  • Cache Events: Listen for Cache::store('redis')->getEventDispatcher()->addListener() (advanced).
  • Testing: Use Cache::shouldReceive('get')->andReturn(...) in PHPUnit with Mockery:
    Cache::shouldReceive('get')
        ->with('key')
        ->andReturn('mocked_value');
    

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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata