diff --git a/src/Bootstrappers/PrefixCacheTenancyBootstrapper.php b/src/Bootstrappers/PrefixCacheTenancyBootstrapper.php index 826ebd59..c03738e8 100644 --- a/src/Bootstrappers/PrefixCacheTenancyBootstrapper.php +++ b/src/Bootstrappers/PrefixCacheTenancyBootstrapper.php @@ -15,10 +15,8 @@ use Stancl\Tenancy\Contracts\Tenant; class PrefixCacheTenancyBootstrapper implements TenancyBootstrapper { protected string|null $originalPrefix = null; - public static array $tenantCacheStores = []; // E.g. 'redis' - public static array $prefixGenerators = [ - // driverName => Closure(Tenant $tenant) - ]; + public static array $tenantCacheStores = []; // E.g. ['redis'] + public static Closure|null $prefixGenerator = null; public function __construct( protected ConfigRepository $config, @@ -30,8 +28,19 @@ class PrefixCacheTenancyBootstrapper implements TenancyBootstrapper { $this->originalPrefix = $this->config->get('cache.prefix'); + // Use default prefix generator if the prefix generator isn't set + static::$prefixGenerator ??= function (Tenant $tenant) { + return $this->originalPrefix . $this->config->get('tenancy.cache.prefix_base') . $tenant->getTenantKey(); + }; + + $prefix = (static::$prefixGenerator)($tenant); + foreach (static::$tenantCacheStores as $store) { - $this->setCachePrefix($store, $this->getStorePrefix($store, $tenant)); + $this->setCachePrefix($store, $prefix); + + // Now that the store uses the passed prefix + // Set the configured prefix back to the default one + $this->config->set('cache.prefix', $this->originalPrefix); } } @@ -40,6 +49,8 @@ class PrefixCacheTenancyBootstrapper implements TenancyBootstrapper foreach (static::$tenantCacheStores as $store) { $this->setCachePrefix($store, $this->originalPrefix); } + + static::$prefixGenerator = null; } protected function setCachePrefix(string $driver, string|null $prefix): void @@ -52,24 +63,11 @@ class PrefixCacheTenancyBootstrapper implements TenancyBootstrapper // It is needed when a call to the facade has been made before bootstrapping tenancy // The facade has its own cache, separate from the container Cache::clearResolvedInstances(); - - // Now that the store uses the passed prefix - // Set the configured prefix back to the default one - $this->config->set('cache.prefix', $this->originalPrefix); } - public function getStorePrefix(string $store, Tenant $tenant): string + public static function generatePrefixUsing(Closure $prefixGenerator): void { - if (isset(static::$prefixGenerators[$store])) { - return static::$prefixGenerators[$store]($tenant); - } - - return $this->originalPrefix . $this->config->get('tenancy.cache.prefix_base') . $tenant->getTenantKey(); - } - - public static function generatePrefixUsing(string $store, Closure $prefixGenerator): void - { - static::$prefixGenerators[$store] = $prefixGenerator; + static::$prefixGenerator = $prefixGenerator; } /** diff --git a/tests/PrefixCacheBootstrapperTest.php b/tests/PrefixCacheBootstrapperTest.php index f0d0ac40..da4800da 100644 --- a/tests/PrefixCacheBootstrapperTest.php +++ b/tests/PrefixCacheBootstrapperTest.php @@ -22,7 +22,7 @@ beforeEach(function () { ]); PrefixCacheTenancyBootstrapper::$tenantCacheStores = [$cacheDriver]; - PrefixCacheTenancyBootstrapper::$prefixGenerators = []; + PrefixCacheTenancyBootstrapper::$prefixGenerator = null; TenancyCacheManager::$addTags = false; @@ -32,7 +32,7 @@ beforeEach(function () { afterEach(function () { PrefixCacheTenancyBootstrapper::$tenantCacheStores = []; - PrefixCacheTenancyBootstrapper::$prefixGenerators = []; + PrefixCacheTenancyBootstrapper::$prefixGenerator = null; TenancyCacheManager::$addTags = true; }); @@ -74,9 +74,12 @@ test('correct cache prefix is used in all contexts', function () { $tenantOnePrefix = $originalPrefix . $prefixBase . $tenant1->getTenantKey(); tenancy()->initialize($tenant1); + // Default prefix generator + $prefixGenerator = PrefixCacheTenancyBootstrapper::$prefixGenerator; + cache()->set('key', 'tenantone-value'); - $expectPrefixToBe($tenantOnePrefix); + $expectPrefixToBe($prefixGenerator($tenant1)); $tenantTwoPrefix = $originalPrefix . $prefixBase . $tenant2->getTenantKey(); @@ -84,7 +87,7 @@ test('correct cache prefix is used in all contexts', function () { cache()->set('key', 'tenanttwo-value'); - $expectPrefixToBe($tenantTwoPrefix); + $expectPrefixToBe($prefixGenerator($tenant2)); // Prefix gets reverted to default after ending tenancy tenancy()->end(); @@ -287,11 +290,11 @@ test('non default stores get prefixed too', function () { // The prefix is the same for both drivers in the central context $tenant = Tenant::create(); $defaultPrefix = cache()->store()->getPrefix(); - $expectedPrefix = config('cache.prefix', '') . config('tenancy.cache.prefix_base', '') . $tenant->getTenantKey(); expect(cache()->store('redis')->getPrefix())->toBe($defaultPrefix); tenancy()->initialize($tenant); + $expectedPrefix = (PrefixCacheTenancyBootstrapper::$prefixGenerator)($tenant); // We didn't add a prefix generator for our 'redis' driver, so we expect the prefix to be generated using the 'default' generator expect(cache()->store()->getPrefix())->toBe($expectedPrefix . ':'); @@ -305,19 +308,17 @@ test('cache store prefix generation can be customized', function() { config(['cache.default' => 'redis']); PrefixCacheTenancyBootstrapper::$tenantCacheStores = ['redis']; - expect(PrefixCacheTenancyBootstrapper::$prefixGenerators)->not()->toHaveKey('redis'); - // Add custom prefix generator for the 'redis' store - PrefixCacheTenancyBootstrapper::generatePrefixUsing('redis', $generateTenantPrefix = function (Tenant $tenant) { + PrefixCacheTenancyBootstrapper::generatePrefixUsing($customPrefixGenerator = function (Tenant $tenant) { return 'redis_tenant_cache_' . $tenant->getTenantKey(); }); - expect(PrefixCacheTenancyBootstrapper::$prefixGenerators)->toHaveKey('redis'); + expect(PrefixCacheTenancyBootstrapper::$prefixGenerator)->toBe($customPrefixGenerator); tenancy()->initialize($tenant = Tenant::create()); // Expect the 'redis' store to use the prefix generated by the custom generator - expect(cache()->store('redis')->getPrefix())->toBe($generateTenantPrefix($tenant) . ':'); + expect(cache()->store('redis')->getPrefix())->toBe($customPrefixGenerator($tenant) . ':'); tenancy()->end(); }); @@ -327,15 +328,18 @@ test('stores get prefixed using the default way if the store does not have a cor // Make 'redis2' the default cache driver config(['cache.default' => 'redis2']); $tenant = Tenant::create(); - $expectedPrefix = config('cache.prefix', '') . config('tenancy.cache.prefix_base', '') . $tenant->getTenantKey(); PrefixCacheTenancyBootstrapper::$tenantCacheStores = ['redis', 'redis2']; // Don't add a generator for 'redis2' // Let the prefix get created using the default approach tenancy()->initialize($tenant); + + $expectedPrefix = (PrefixCacheTenancyBootstrapper::$prefixGenerator)($tenant); + expect(cache()->store()->getPrefix())->toBe($expectedPrefix . ':'); // Other stores without a prefix generator use the default generator too expect(cache()->store('redis')->getPrefix())->toBe($expectedPrefix . ':'); + tenancy()->end(); });