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.
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.)
Wrap a PSR-6 Pool:
use Cache\PrefixedCachePool;
use PhpCache\RedisCachePool;
$redisPool = new RedisCachePool(new Redis());
$prefixedPool = new PrefixedCachePool($redisPool, 'tenant_1_');
Use in Laravel:
public function __construct(PrefixedCachePool $cache) { ... }
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'
tests/ for edge cases (e.g., empty prefixes, special characters).spatie/laravel-cache-prefix for a pre-built Laravel wrapper if needed.// In a multi-tenant app, prefix by tenant ID
$tenantId = Auth::user()->tenant_id;
$prefixedPool = new PrefixedCachePool($redisPool, "tenant_{$tenantId}_");
// Isolate experimental features
$prefixedPool = new PrefixedCachePool($redisPool, 'feature_new_ui_');
Cache::store('feature_new_ui')->put('settings', $config);
// Prefix by module (e.g., 'api_v1_', 'analytics_')
$modulePrefix = config("cache.modules.{$module}");
$prefixedPool = new PrefixedCachePool($redisPool, $modulePrefix);
// Use different prefixes per environment
$envPrefix = app()->environment() === 'staging' ? 'staging_' : 'prod_';
$prefixedPool = new PrefixedCachePool($redisPool, $envPrefix);
Define a Custom Driver (config/cache.php):
'stores' => [
'prefixed_redis' => [
'driver' => 'prefixed',
'pool' => 'redis',
'prefix' => env('CACHE_PREFIX', 'app_'),
],
],
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']);
});
}
Use the Store:
Cache::store('prefixed_redis')->put('key', 'value');
// Internally stored as 'app_key'
// 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;
}
// Prefix queue job payloads to avoid collisions
$prefixedPool = new PrefixedCachePool($redisPool, 'jobs_');
$payload = $prefixedPool->get('job_123'); // Key: 'jobs_job_123'
Avoid Hardcoding Prefixes: Use environment variables or config files for flexibility:
$prefix = config('cache.prefixes.default');
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.
Leverage PSR-6 Adapters:
php-cache/redis-cachephp-cache/memcached-cachephp-cache/file-cacheTagging 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
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_');
Key Length Limits:
if (strlen($prefix) > 200) {
throw new \InvalidArgumentException('Prefix too long');
}
Prefix Collisions:
tenant_1_), keys will collide.Cache Invalidation Issues:
// Wrong: Uses global key
Cache::forget('user_123'); // Should be 'tenant_1_user_123'
Non-String Prefixes:
$prefix = (string) $tenantId . '_';
Laravel’s Cache::remember():
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);
Inspect Stored Keys:
KEYS tenant_1_* # List all prefixed keys
stats items | grep tenant_1_
Log Prefixed Keys:
$prefixedPool->getItem('key')->set('value')->expiresAfter(60);
logger()->debug("Stored key: " . $prefixedPool->getItem('key')->getKey());
Check for Silent Failures:
$memcached = new Memcached();
$memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
$memcached->setOption(Memcached::OPT_BEHAVIOR, Memcached::BEHAVIOR_NO_BLOCK);
Test Edge Cases:
$prefixedPool = new PrefixedCachePool($redisPool, '');
// Keys are stored without modification.
$prefixedPool = new
How can I help you explore Laravel packages today?