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

Prefixed Cache Laravel Package

cache/prefixed-cache

PSR-6 cache pool decorator that prefixes all cache item keys with a predefined string. Wrap any PSR-6 cache (e.g., Redis) to safely namespace entries per app, tenant, or module. Part of the PHP-Cache ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package:

    composer require cache/prefixed-cache php-cache/redis-cache
    

    (Replace redis-cache with your preferred PSR-6 adapter, e.g., memcached-cache or file-cache.)

  2. Wrap a PSR-6 Pool:

    use Cache\PrefixedCachePool;
    use PhpCache\RedisCachePool;
    
    $redisPool = new RedisCachePool(new Redis());
    $prefixedPool = new PrefixedCachePool($redisPool, 'tenant_1_');
    
  3. Use in Laravel:

    • Option A: Manually inject the prefixed pool into services:
      public function __construct(PrefixedCachePool $cache) { ... }
      
    • Option B: Extend Laravel’s cache configuration (see Implementation Patterns).
  4. First Use Case: Replace a global cache call with a prefixed one:

    // Before (global key)
    Cache::put('user_123', $data);
    
    // After (prefixed key)
    $prefixedPool->put('user_123', $data); // Stores as 'tenant_1_user_123'
    

Where to Look First

  • README.md: Confirm PSR-6 compatibility with your backend (Redis, Memcached, etc.).
  • Tests: Review tests/ for edge cases (e.g., empty prefixes, special characters).
  • Laravel Integration: Check spatie/laravel-cache-prefix for a pre-built Laravel wrapper if needed.

Implementation Patterns

Usage Patterns

1. Per-Tenant Isolation

// In a multi-tenant app, prefix by tenant ID
$tenantId = Auth::user()->tenant_id;
$prefixedPool = new PrefixedCachePool($redisPool, "tenant_{$tenantId}_");

2. Feature Flagging

// Isolate experimental features
$prefixedPool = new PrefixedCachePool($redisPool, 'feature_new_ui_');
Cache::store('feature_new_ui')->put('settings', $config);

3. Modular Caching

// Prefix by module (e.g., 'api_v1_', 'analytics_')
$modulePrefix = config("cache.modules.{$module}");
$prefixedPool = new PrefixedCachePool($redisPool, $modulePrefix);

4. Environment-Specific Caches

// Use different prefixes per environment
$envPrefix = app()->environment() === 'staging' ? 'staging_' : 'prod_';
$prefixedPool = new PrefixedCachePool($redisPool, $envPrefix);

Workflows

Laravel Cache Store Extension

  1. Define a Custom Driver (config/cache.php):

    'stores' => [
        'prefixed_redis' => [
            'driver' => 'prefixed',
            'pool' => 'redis',
            'prefix' => env('CACHE_PREFIX', 'app_'),
        ],
    ],
    
  2. Extend CacheManager (app/Providers/AppServiceProvider.php):

    use Cache\PrefixedCachePool;
    use Illuminate\Cache\CacheManager;
    
    public function register()
    {
        Cache::extend('prefixed', function ($app, $config) {
            $pool = Cache::store($config['pool'])->getStore();
            return new PrefixedCachePool($pool, $config['prefix']);
        });
    }
    
  3. Use the Store:

    Cache::store('prefixed_redis')->put('key', 'value');
    // Internally stored as 'app_key'
    

Dependency Injection

// services.php
$app->bind(PrefixedCachePool::class, function ($app) {
    $pool = new RedisCachePool(new Redis());
    return new PrefixedCachePool($pool, 'api_');
});

// In a service
public function __construct(PrefixedCachePool $cache) {
    $this->cache = $cache;
}

Queue Job Payloads

// Prefix queue job payloads to avoid collisions
$prefixedPool = new PrefixedCachePool($redisPool, 'jobs_');
$payload = $prefixedPool->get('job_123'); // Key: 'jobs_job_123'

Integration Tips

  1. Avoid Hardcoding Prefixes: Use environment variables or config files for flexibility:

    $prefix = config('cache.prefixes.default');
    
  2. Combine with Laravel’s Cache::prefix():

    // PrefixedCachePool + Cache::prefix() = Double prefixing (avoid)
    $prefixedPool = new PrefixedCachePool($redisPool, 'tenant_');
    Cache::store('prefixed_redis')->prefix('users')->put('1', $data);
    // Result: 'tenant_users_1' (unintended)
    

    Solution: Use either PrefixedCachePool or Cache::prefix(), not both.

  3. Leverage PSR-6 Adapters:

    • For Redis: php-cache/redis-cache
    • For Memcached: php-cache/memcached-cache
    • For Filesystem: php-cache/file-cache
  4. Tagging Integration: If using PHP-Cache’s tagging system, ensure prefixes don’t interfere with tag patterns:

    // Example: Prefix + Tag
    $prefixedPool = new PrefixedCachePool($redisPool, 'tenant_');
    $item = $prefixedPool->getItem('user_1');
    $item->setTags(['tenant:1', 'user']); // Tags work alongside prefixes
    
  5. Fallback for Non-PSR-6 Caches: If using raw Redis/Memcached, wrap the client first:

    $redis = new Redis();
    $psr6Pool = new RedisCachePool($redis);
    $prefixedPool = new PrefixedCachePool($psr6Pool, 'fallback_');
    

Gotchas and Tips

Pitfalls

  1. Key Length Limits:

    • Redis keys have a 512MB limit, but practical limits are ~449 bytes.
    • Fix: Validate prefix length in config:
      if (strlen($prefix) > 200) {
          throw new \InvalidArgumentException('Prefix too long');
      }
      
  2. Prefix Collisions:

    • If two services use the same prefix (e.g., tenant_1_), keys will collide.
    • Fix: Use unique prefixes (e.g., include a service ID or timestamp).
  3. Cache Invalidation Issues:

    • Forgetting to prefix keys during invalidation:
      // Wrong: Uses global key
      Cache::forget('user_123'); // Should be 'tenant_1_user_123'
      
    • Fix: Always use the prefixed pool for invalidation.
  4. Non-String Prefixes:

    • Prefixes must be strings. Passing non-strings (e.g., integers) may cause errors.
    • Fix: Cast prefixes to strings:
      $prefix = (string) $tenantId . '_';
      
  5. Laravel’s Cache::remember():

    • The remember method uses the store’s default prefix. Ensure consistency:
      // Correct: Uses prefixed pool
      Cache::store('prefixed_redis')->remember('key', 60, fn() => $data);
      
      // Incorrect: May use global prefix
      Cache::remember('key', 60, fn() => $data);
      

Debugging

  1. Inspect Stored Keys:

    • For Redis:
      KEYS tenant_1_*  # List all prefixed keys
      
    • For Memcached:
      stats items | grep tenant_1_
      
  2. Log Prefixed Keys:

    $prefixedPool->getItem('key')->set('value')->expiresAfter(60);
    logger()->debug("Stored key: " . $prefixedPool->getItem('key')->getKey());
    
  3. Check for Silent Failures:

    • Some backends (e.g., Memcached) may silently truncate keys. Enable strict mode:
      $memcached = new Memcached();
      $memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
      $memcached->setOption(Memcached::OPT_BEHAVIOR, Memcached::BEHAVIOR_NO_BLOCK);
      
  4. Test Edge Cases:

    • Empty prefixes:
      $prefixedPool = new PrefixedCachePool($redisPool, '');
      // Keys are stored without modification.
      
    • Special characters:
      $prefixedPool = new
      
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.
boundwize/pyrameter
testo/facade
headercat/phpstan-extension-ide-helper
yosymfony/parser-utils
innmind/black-box
babenkoivan/elastic-migrations
babenkoivan/elastic-adapter
sandermuller/package-boost-php
sandermuller/boost-core
depa/sulu-google-reviews-bundle
croct/plug-symfony
develia/commons
dmstr/symfony-system-resources-bundle
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
renatomarinho/laravel-page-speed
develia/geo-bundle
austinheap/laravel-database-encryption
dreamzy/livewire-charts
touchestate-sdk/php-sdk