From ef3a233cee9bbf7bd4e13c307c774c22b5e73b44 Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:08:59 +0400 Subject: [PATCH 1/9] Using repository instead of store to check driver first --- src/Console/Commands/ClearCommand.php | 4 ++-- src/Console/Commands/StatusCommand.php | 2 +- tests/Unit/Console/ClearCommandTest.php | 14 +++++++------- tests/Unit/Console/StatusCommandTest.php | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Console/Commands/ClearCommand.php b/src/Console/Commands/ClearCommand.php index a8e7338..e750275 100644 --- a/src/Console/Commands/ClearCommand.php +++ b/src/Console/Commands/ClearCommand.php @@ -50,7 +50,7 @@ protected function clearSpecificKey(SmartCache $cache, string $key): int $success = $cache->forget($key); } else { $this->info("Clearing cache item with key '{$key}' (not managed by SmartCache)..."); - $success = $cache->store()->forget($key); + $success = $cache->repository()->forget($key); } if ($success) { @@ -112,7 +112,7 @@ protected function clearAllKeys(SmartCache $cache): int protected function clearOrphanedKeys(SmartCache $cache): int { - $repository = $cache->store(); + $repository = $cache->repository(); $store = $repository->getStore(); $cleared = 0; $managedKeys = $cache->getManagedKeys(); diff --git a/src/Console/Commands/StatusCommand.php b/src/Console/Commands/StatusCommand.php index 36824fa..2fff4e6 100644 --- a/src/Console/Commands/StatusCommand.php +++ b/src/Console/Commands/StatusCommand.php @@ -123,7 +123,7 @@ protected function displayConfiguration(ConfigRepository $config): void protected function findAllNonManagedKeys(SmartCache $cache): array { - $repository = $cache->store(); + $repository = $cache->repository(); $store = $repository->getStore(); $managedKeys = $cache->getManagedKeys(); $nonManagedKeys = []; diff --git a/tests/Unit/Console/ClearCommandTest.php b/tests/Unit/Console/ClearCommandTest.php index e992580..b6d07ab 100644 --- a/tests/Unit/Console/ClearCommandTest.php +++ b/tests/Unit/Console/ClearCommandTest.php @@ -63,15 +63,15 @@ public function test_clear_command_with_no_managed_keys_and_force() ->twice() // Called once for display, once in clearOrphanedKeys ->andReturn([]); - // Mock the store to return a cache repository that will simulate an unsupported driver + // Mock the repository to return a cache repository that will simulate an unsupported driver $mockRepository = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); $mockStore = Mockery::mock(\stdClass::class); // Use stdClass to simulate unsupported driver - + $mockRepository->shouldReceive('getStore') ->once() ->andReturn($mockStore); - - $this->mockSmartCache->shouldReceive('store') + + $this->mockSmartCache->shouldReceive('repository') ->once() ->andReturn($mockRepository); @@ -303,14 +303,14 @@ public function test_clear_specific_key_not_managed_but_exists_with_force() ->with($existingKey) ->andReturn(true); - // Mock the store() method to return a cache repository + // Mock the repository() method to return a cache repository $mockStore = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); $mockStore->shouldReceive('forget') ->once() ->with($existingKey) ->andReturn(true); - - $this->mockSmartCache->shouldReceive('store') + + $this->mockSmartCache->shouldReceive('repository') ->once() ->andReturn($mockStore); diff --git a/tests/Unit/Console/StatusCommandTest.php b/tests/Unit/Console/StatusCommandTest.php index e511c48..091a9bb 100644 --- a/tests/Unit/Console/StatusCommandTest.php +++ b/tests/Unit/Console/StatusCommandTest.php @@ -301,18 +301,18 @@ public function test_status_command_with_force_option_no_orphaned_keys() ->once() ->andReturn($this->getDefaultSmartCacheConfig()); - // Mock the store() method to return a cache repository + // Mock the repository() method to return a cache repository $mockRepository = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); $mockStore = Mockery::mock(\Illuminate\Cache\ArrayStore::class); - + $mockRepository->shouldReceive('getStore') ->once() ->andReturn($mockStore); - - $this->mockSmartCache->shouldReceive('store') + + $this->mockSmartCache->shouldReceive('repository') ->once() ->andReturn($mockRepository); - + // Mock has() calls to check if managed keys exist $this->mockSmartCache->shouldReceive('has') ->with('key1') @@ -355,18 +355,18 @@ public function test_status_command_with_force_option_finds_missing_managed_keys ->once() ->andReturn($this->getDefaultSmartCacheConfig()); - // Mock the store() method to return a cache repository + // Mock the repository() method to return a cache repository $mockRepository = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); $mockStore = Mockery::mock(\Illuminate\Cache\ArrayStore::class); - + $mockRepository->shouldReceive('getStore') ->once() ->andReturn($mockStore); - - $this->mockSmartCache->shouldReceive('store') + + $this->mockSmartCache->shouldReceive('repository') ->once() ->andReturn($mockRepository); - + // Mock has() calls - one key exists, two are missing $this->mockSmartCache->shouldReceive('has') ->with('existing-key') From a1a73d6c8f3bd75c44890c5a97e140392495f9ab Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:09:51 +0400 Subject: [PATCH 2/9] Implement repository and update store to support driver selection --- src/Contracts/SmartCache.php | 20 ++++++++++++++++++-- src/Facades/SmartCache.php | 5 +++-- src/SmartCache.php | 29 +++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/Contracts/SmartCache.php b/src/Contracts/SmartCache.php index 0fbe0c0..3c6a689 100644 --- a/src/Contracts/SmartCache.php +++ b/src/Contracts/SmartCache.php @@ -68,11 +68,27 @@ public function remember(string $key, $ttl, \Closure $callback): mixed; public function rememberForever(string $key, \Closure $callback): mixed; /** - * Get the underlying cache store. + * Get a SmartCache instance using a specific cache store. * + * When called without arguments, returns the current instance. + * When called with a store name, returns a new SmartCache instance + * configured to use that store while maintaining all optimization strategies. + * + * @param string|null $name The cache store name (e.g., 'redis', 'file', 'memcached') + * @return static + */ + public function store(string|null $name = null): static; + + /** + * Get the underlying cache repository directly. + * + * This provides raw access to Laravel's cache repository without SmartCache optimizations. + * Use this when you need direct access to the cache driver. + * + * @param string|null $name The store name (null for current store) * @return \Illuminate\Contracts\Cache\Repository */ - public function store(string|null $name = null): \Illuminate\Contracts\Cache\Repository; + public function repository(string|null $name = null): \Illuminate\Contracts\Cache\Repository; /** * Clear all cache keys managed by SmartCache. diff --git a/src/Facades/SmartCache.php b/src/Facades/SmartCache.php index 8a9821d..f6f961c 100644 --- a/src/Facades/SmartCache.php +++ b/src/Facades/SmartCache.php @@ -16,7 +16,8 @@ * @method static mixed swr(string $key, \Closure $callback, int $ttl = 3600, int $staleTtl = 7200) * @method static mixed stale(string $key, \Closure $callback, int $ttl = 1800, int $staleTtl = 86400) * @method static mixed refreshAhead(string $key, \Closure $callback, int $ttl = 3600, int $refreshWindow = 600) - * @method static \Illuminate\Contracts\Cache\Repository store(string|null $name = null) + * @method static \SmartCache\SmartCache store(string|null $name = null) + * @method static \Illuminate\Contracts\Cache\Repository repository(string|null $name = null) * @method static bool clear() * @method static array getManagedKeys() * @method static static tags(string|array $tags) @@ -35,7 +36,7 @@ * @method static array analyzePerformance() * @method static int cleanupExpiredManagedKeys() * @method static bool hasFeature(string $feature) - * + * * @see \SmartCache\SmartCache */ class SmartCache extends Facade diff --git a/src/SmartCache.php b/src/SmartCache.php index 5e3144a..46fe1ec 100644 --- a/src/SmartCache.php +++ b/src/SmartCache.php @@ -334,12 +334,37 @@ public function rememberForever(string $key, \Closure $callback): mixed /** * {@inheritdoc} */ - public function store(string|null $name = null): Repository + public function store(string|null $name = null): static + { + if ($name === null) { + return $this; + } + + // Create a new SmartCache instance with the specified store + // This preserves all optimization strategies while using a different cache driver + return new static( + $this->cacheManager->store($name), + $this->cacheManager, + $this->config, + $this->strategies + ); + } + + /** + * Get the underlying cache repository. + * + * This provides direct access to the Laravel cache repository without SmartCache optimizations. + * Use this when you need raw access to the cache driver. + * + * @param string|null $name The store name (null for current store) + * @return Repository + */ + public function repository(string|null $name = null): Repository { if ($name === null) { return $this->cache; } - + return $this->cacheManager->store($name); } From eb1f2c276f379cb5253e4958824e5854cbc882cc Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:10:09 +0400 Subject: [PATCH 3/9] Extending tests for using driver on store --- tests/Unit/SmartCacheTest.php | 99 +++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/tests/Unit/SmartCacheTest.php b/tests/Unit/SmartCacheTest.php index 8eb1990..bfc4c6b 100644 --- a/tests/Unit/SmartCacheTest.php +++ b/tests/Unit/SmartCacheTest.php @@ -244,15 +244,104 @@ public function test_managed_keys_tracking() $this->assertContains($key2, $managedKeys); } - public function test_can_get_different_cache_stores() + public function test_store_returns_smart_cache_instance() { - // Test getting default store + // Test getting default store returns self $defaultStore = $this->smartCache->store(); - $this->assertEquals($this->getCacheStore(), $defaultStore); + $this->assertInstanceOf(\SmartCache\SmartCache::class, $defaultStore); + $this->assertSame($this->smartCache, $defaultStore); - // Test getting named store + // Test getting named store returns a new SmartCache instance $arrayStore = $this->smartCache->store('array'); - $this->assertNotNull($arrayStore); + $this->assertInstanceOf(\SmartCache\SmartCache::class, $arrayStore); + $this->assertNotSame($this->smartCache, $arrayStore); + } + + public function test_store_method_preserves_optimization_strategies() + { + // Create a SmartCache instance with compression enabled + $smartCache = new SmartCache( + $this->getCacheStore(), + $this->getCacheManager(), + $this->app['config'], + [new CompressionStrategy(1024, 6)] + ); + + // Get a store instance for array driver + $arraySmartCache = $smartCache->store('array'); + + // Store large data through the new store instance + $key = 'store-optimization-test'; + $value = $this->createCompressibleData(); + + $arraySmartCache->put($key, $value); + + // Verify the value is compressed in the raw cache + $rawCached = $this->getCacheStore('array')->get($key); + $this->assertValueIsCompressed($rawCached); + + // Verify we can retrieve the original value + $retrieved = $arraySmartCache->get($key); + $this->assertEquals($value, $retrieved); + } + + public function test_store_method_allows_chaining_operations() + { + // Test that store() allows chaining cache operations + $key = 'chained-store-key'; + $value = 'chained-value'; + + // Put using chained store + $this->smartCache->store('array')->put($key, $value, 3600); + + // Get using chained store + $retrieved = $this->smartCache->store('array')->get($key); + $this->assertEquals($value, $retrieved); + + // Remember using chained store + $rememberValue = $this->smartCache->store('array')->remember('remember-chain-key', 3600, fn() => 'remembered'); + $this->assertEquals('remembered', $rememberValue); + } + + public function test_store_method_uses_correct_driver() + { + $key = 'driver-test-key'; + $value = 'test-value'; + + // Store in array driver via store() method + $this->smartCache->store('array')->put($key, $value); + + // Value should be in array store + $this->assertTrue($this->smartCache->store('array')->has($key)); + + // Value should not be in file store (different driver) + $this->assertFalse($this->smartCache->store('file')->has($key)); + } + + public function test_repository_method_returns_raw_cache() + { + // Test getting default repository + $defaultRepo = $this->smartCache->repository(); + $this->assertInstanceOf(\Illuminate\Contracts\Cache\Repository::class, $defaultRepo); + $this->assertEquals($this->getCacheStore(), $defaultRepo); + + // Test getting named repository + $arrayRepo = $this->smartCache->repository('array'); + $this->assertInstanceOf(\Illuminate\Contracts\Cache\Repository::class, $arrayRepo); + } + + public function test_repository_bypasses_optimization() + { + $key = 'repository-bypass-test'; + $value = $this->createCompressibleData(); // Large, compressible data + + // Store via repository (should bypass SmartCache optimization) + $this->smartCache->repository()->put($key, $value); + + // Get raw value - should NOT be compressed since we used repository + $rawCached = $this->getCacheStore()->get($key); + $this->assertEquals($value, $rawCached); + $this->assertIsString($rawCached); // Not wrapped in optimization array } public function test_chunked_data_cleanup_on_forget() From 3f6da1c358f60aeb6b80b09612609e55b4994783 Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:10:30 +0400 Subject: [PATCH 4/9] Docs updated for new store method --- README.md | 19 +++++++++++++ docs/index.html | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/README.md b/README.md index cbed213..87197b2 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,25 @@ smart_cache(['products' => $products], 3600); $products = smart_cache('products'); ``` +### Using Different Cache Drivers + +Use different cache drivers while maintaining all SmartCache optimizations: + +```php +// Use Redis with all SmartCache optimizations (compression, chunking, etc.) +SmartCache::store('redis')->put('key', $value, 3600); +SmartCache::store('redis')->get('key'); + +// Use Memcached with optimizations +SmartCache::store('memcached')->remember('users', 3600, fn() => User::all()); + +// Use file cache with optimizations +SmartCache::store('file')->put('config', $config, 86400); + +// For raw access to Laravel's cache (bypasses SmartCache optimizations) +SmartCache::repository('redis')->put('key', $value, 3600); +``` + ## 💡 Core Features (Automatic Optimization) ### 1. Intelligent Compression diff --git a/docs/index.html b/docs/index.html index ed277ed..4a4c91d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -763,6 +763,31 @@

Familiar Laravel API

// Clear all cache SmartCache::flush(); +

Using Different Cache Drivers

+

Use different cache drivers while maintaining all SmartCache optimizations:

+ +
// Use Redis with all SmartCache optimizations (compression, chunking, etc.)
+SmartCache::store('redis')->put('key', $value, 3600);
+SmartCache::store('redis')->get('key');
+
+// Use Memcached with optimizations
+SmartCache::store('memcached')->remember('users', 3600, fn() => User::all());
+
+// Use file cache with optimizations
+SmartCache::store('file')->put('config', $config, 86400);
+
+// Chain multiple operations on a specific store
+$redisCache = SmartCache::store('redis');
+$redisCache->put('users', $users, 3600);
+$redisCache->put('products', $products, 3600);
+
+// For raw access to Laravel's cache (bypasses SmartCache optimizations)
+SmartCache::repository('redis')->put('key', $value, 3600);
+ +
+ Note: The store() method returns a SmartCache instance, so all optimization strategies (compression, chunking, encryption, etc.) continue to work. Use repository() if you need direct access to Laravel's cache without SmartCache optimizations. +
+

Automatic Optimization

SmartCache automatically optimizes your data when beneficial:

@@ -1712,6 +1737,55 @@

🔧 Basic Cache Operations

+
+
SmartCache::store() Enhanced!
+
SmartCache::store(string|null $name = null): static
+

Get a SmartCache instance for a specific cache driver. All SmartCache optimizations (compression, chunking, encryption, etc.) are preserved.

+
+ $name (string|null) - The cache store name (redis, file, memcached, etc.) +
+
+ Returns: static - SmartCache instance configured for the specified store +
+
+ Examples: +
// Use Redis with all SmartCache optimizations
+SmartCache::store('redis')->put('key', $value, 3600);
+SmartCache::store('redis')->get('key');
+
+// Use Memcached with optimizations
+SmartCache::store('memcached')->remember('users', 3600, fn() => User::all());
+
+// Chain operations on a specific store
+$redisCache = SmartCache::store('redis');
+$redisCache->put('users', $users, 3600);
+$redisCache->put('products', $products, 3600);
+
+
+ +
+
SmartCache::repository() New!
+
SmartCache::repository(string|null $name = null): \Illuminate\Contracts\Cache\Repository
+

Get direct access to Laravel's underlying cache repository. This bypasses all SmartCache optimizations.

+
+ $name (string|null) - The cache store name (redis, file, memcached, etc.) +
+
+ Returns: Repository - Laravel's native cache repository +
+
+ Examples: +
// Direct access to Redis cache (no SmartCache optimizations)
+SmartCache::repository('redis')->put('key', $value, 3600);
+
+// Direct access to default cache store
+SmartCache::repository()->get('key');
+
+
+ Note: Use repository() only when you need to bypass SmartCache optimizations. For normal usage, prefer store() to maintain all optimization benefits. +
+
+

🌊 SWR Patterns (Laravel 12+)

From c95211ec5704ce64d36701b845dae0836b9004ba Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:44:56 +0400 Subject: [PATCH 5/9] Add Repository interface to SmartCache --- src/Contracts/SmartCache.php | 92 +++--------------- src/SmartCache.php | 175 +++++++++++++++++++++++++++++++---- 2 files changed, 167 insertions(+), 100 deletions(-) diff --git a/src/Contracts/SmartCache.php b/src/Contracts/SmartCache.php index 3c6a689..f1610aa 100644 --- a/src/Contracts/SmartCache.php +++ b/src/Contracts/SmartCache.php @@ -2,71 +2,16 @@ namespace SmartCache\Contracts; -interface SmartCache +use Illuminate\Contracts\Cache\Repository; + +/** + * SmartCache Contract + * + * This interface extends Laravel's Repository interface to ensure full compatibility + * with Laravel's cache system while adding SmartCache-specific optimization features. + */ +interface SmartCache extends Repository { - /** - * Get an item from the cache. - * - * @param string $key - * @param mixed $default - * @return mixed - */ - public function get(string $key, mixed $default = null): mixed; - - /** - * Store an item in the cache. - * - * @param string $key - * @param mixed $value - * @param \DateTimeInterface|\DateInterval|int|null $ttl - * @return bool - */ - public function put(string $key, mixed $value, $ttl = null): bool; - - /** - * Determine if an item exists in the cache. - * - * @param string $key - * @return bool - */ - public function has(string $key): bool; - - /** - * Remove an item from the cache. - * - * @param string $key - * @return bool - */ - public function forget(string $key): bool; - - /** - * Store an item in the cache indefinitely. - * - * @param string $key - * @param mixed $value - * @return bool - */ - public function forever(string $key, mixed $value): bool; - - /** - * Get an item from the cache, or execute the given Closure and store the result. - * - * @param string $key - * @param \DateTimeInterface|\DateInterval|int|null $ttl - * @param \Closure $callback - * @return mixed - */ - public function remember(string $key, $ttl, \Closure $callback): mixed; - - /** - * Get an item from the cache, or execute the given Closure and store the result forever. - * - * @param string $key - * @param \Closure $callback - * @return mixed - */ - public function rememberForever(string $key, \Closure $callback): mixed; - /** * Get a SmartCache instance using a specific cache store. * @@ -86,16 +31,9 @@ public function store(string|null $name = null): static; * Use this when you need direct access to the cache driver. * * @param string|null $name The store name (null for current store) - * @return \Illuminate\Contracts\Cache\Repository - */ - public function repository(string|null $name = null): \Illuminate\Contracts\Cache\Repository; - - /** - * Clear all cache keys managed by SmartCache. - * - * @return bool + * @return Repository */ - public function clear(): bool; + public function repository(string|null $name = null): Repository; /** * Get all keys managed by SmartCache. @@ -307,14 +245,6 @@ public function many(array $keys): array; */ public function putMany(array $values, $ttl = null): bool; - /** - * Remove multiple items from the cache. - * - * @param array $keys - * @return bool - */ - public function deleteMultiple(array $keys): bool; - /** * Get a memoized cache instance. * diff --git a/src/SmartCache.php b/src/SmartCache.php index 46fe1ec..920d909 100644 --- a/src/SmartCache.php +++ b/src/SmartCache.php @@ -3,6 +3,7 @@ namespace SmartCache; use Illuminate\Contracts\Cache\Repository; +use Illuminate\Contracts\Cache\Store; use Illuminate\Contracts\Cache\Factory as CacheManager; use Illuminate\Contracts\Config\Repository as ConfigRepository; use Illuminate\Support\Facades\Log; @@ -16,7 +17,7 @@ use SmartCache\Traits\HasLocks; use SmartCache\Traits\DispatchesCacheEvents; -class SmartCache implements SmartCacheContract +class SmartCache implements SmartCacheContract, Repository { use HasLocks, DispatchesCacheEvents; /** @@ -170,9 +171,9 @@ public function addStrategy(OptimizationStrategy $strategy): self /** * {@inheritdoc} */ - public function get(string $key, mixed $default = null): mixed + public function get($key, $default = null): mixed { - $key = $this->applyNamespace($key); + $key = $this->applyNamespace((string) $key); $startTime = $this->enablePerformanceMonitoring ? microtime(true) : null; $value = $this->cache->get($key, null); @@ -212,9 +213,9 @@ protected function trackAccessFrequency(string $key): void /** * {@inheritdoc} */ - public function put(string $key, mixed $value, $ttl = null): bool + public function put($key, $value, $ttl = null): bool { - $key = $this->applyNamespace($key); + $key = $this->applyNamespace((string) $key); $startTime = $this->enablePerformanceMonitoring ? microtime(true) : null; $optimizedValue = $this->maybeOptimizeValue($value, $key, $ttl); @@ -241,21 +242,45 @@ public function put(string $key, mixed $value, $ttl = null): bool return $result; } + /** + * PSR-16 alias for put(). + * + * @param string $key + * @param mixed $value + * @param \DateTimeInterface|\DateInterval|int|null $ttl + * @return bool + */ + public function set($key, $value, $ttl = null): bool + { + return $this->put($key, $value, $ttl); + } + /** * {@inheritdoc} */ - public function has(string $key): bool + public function has($key): bool { - $key = $this->applyNamespace($key); + $key = $this->applyNamespace((string) $key); return $this->cache->has($key); } + /** + * PSR-16 alias for forget(). + * + * @param string $key + * @return bool + */ + public function delete($key): bool + { + return $this->forget($key); + } + /** * {@inheritdoc} */ - public function forget(string $key): bool + public function forget($key): bool { - $key = $this->applyNamespace($key); + $key = $this->applyNamespace((string) $key); $value = $this->cache->get($key); // If value is chunked, clean up all chunk keys @@ -285,9 +310,9 @@ public function forget(string $key): bool /** * {@inheritdoc} */ - public function forever(string $key, mixed $value): bool + public function forever($key, $value): bool { - $key = $this->applyNamespace($key); + $key = $this->applyNamespace((string) $key); $optimizedValue = $this->maybeOptimizeValue($value, $key, null); // Track all keys for pattern matching and invalidation @@ -304,30 +329,43 @@ public function forever(string $key, mixed $value): bool /** * {@inheritdoc} */ - public function remember(string $key, $ttl, \Closure $callback): mixed + public function remember($key, $ttl, \Closure $callback): mixed { if ($this->has($key)) { return $this->get($key); } - + $value = $callback(); $this->put($key, $value, $ttl); - + return $value; } + /** + * Get an item from the cache, or execute the given Closure and store the result forever. + * This is an alias for rememberForever(). + * + * @param string $key + * @param \Closure $callback + * @return mixed + */ + public function sear($key, \Closure $callback): mixed + { + return $this->rememberForever($key, $callback); + } + /** * {@inheritdoc} */ - public function rememberForever(string $key, \Closure $callback): mixed + public function rememberForever($key, \Closure $callback): mixed { if ($this->has($key)) { return $this->get($key); } - + $value = $callback(); $this->forever($key, $value); - + return $value; } @@ -368,6 +406,105 @@ public function repository(string|null $name = null): Repository return $this->cacheManager->store($name); } + /** + * {@inheritdoc} + */ + public function getStore(): Store + { + return $this->cache->getStore(); + } + + /** + * Retrieve an item from the cache and delete it. + * + * @param array|string $key + * @param mixed $default + * @return mixed + */ + public function pull($key, $default = null): mixed + { + $value = $this->get($key, $default); + $this->forget($key); + return $value; + } + + /** + * Store an item in the cache if the key does not exist. + * + * @param string $key + * @param mixed $value + * @param \DateTimeInterface|\DateInterval|int|null $ttl + * @return bool + */ + public function add($key, $value, $ttl = null): bool + { + if ($this->has($key)) { + return false; + } + + return $this->put($key, $value, $ttl); + } + + /** + * Increment the value of an item in the cache. + * + * @param string $key + * @param mixed $value + * @return int|bool + */ + public function increment($key, $value = 1): int|bool + { + $key = $this->applyNamespace((string) $key); + return $this->cache->increment($key, $value); + } + + /** + * Decrement the value of an item in the cache. + * + * @param string $key + * @param mixed $value + * @return int|bool + */ + public function decrement($key, $value = 1): int|bool + { + $key = $this->applyNamespace((string) $key); + return $this->cache->decrement($key, $value); + } + + /** + * PSR-16: Obtains multiple cache items identified by their unique keys. + * + * @param iterable $keys + * @param mixed $default + * @return iterable + */ + public function getMultiple($keys, $default = null): iterable + { + $results = []; + foreach ($keys as $key) { + $results[$key] = $this->get($key, $default); + } + return $results; + } + + /** + * PSR-16: Persists a set of key => value pairs in the cache. + * + * @param iterable $values + * @param \DateTimeInterface|\DateInterval|int|null $ttl + * @return bool + */ + public function setMultiple($values, $ttl = null): bool + { + $success = true; + foreach ($values as $key => $value) { + if (!$this->put($key, $value, $ttl)) { + $success = false; + } + } + return $success; + } + /** * {@inheritdoc} */ @@ -1810,10 +1947,10 @@ public function putMany(array $values, $ttl = null): bool /** * Remove multiple items from the cache. * - * @param array $keys + * @param iterable $keys * @return bool */ - public function deleteMultiple(array $keys): bool + public function deleteMultiple($keys): bool { $success = true; From e22837998c15dc26d77c30beb7ac344a5438cd92 Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:45:04 +0400 Subject: [PATCH 6/9] Update console commands to use getStore() --- src/Console/Commands/ClearCommand.php | 7 +++---- src/Console/Commands/StatusCommand.php | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Console/Commands/ClearCommand.php b/src/Console/Commands/ClearCommand.php index e750275..861c1cf 100644 --- a/src/Console/Commands/ClearCommand.php +++ b/src/Console/Commands/ClearCommand.php @@ -50,7 +50,7 @@ protected function clearSpecificKey(SmartCache $cache, string $key): int $success = $cache->forget($key); } else { $this->info("Clearing cache item with key '{$key}' (not managed by SmartCache)..."); - $success = $cache->repository()->forget($key); + $success = $cache->store()->forget($key); } if ($success) { @@ -112,8 +112,7 @@ protected function clearAllKeys(SmartCache $cache): int protected function clearOrphanedKeys(SmartCache $cache): int { - $repository = $cache->repository(); - $store = $repository->getStore(); + $store = $cache->getStore(); $cleared = 0; $managedKeys = $cache->getManagedKeys(); @@ -122,7 +121,7 @@ protected function clearOrphanedKeys(SmartCache $cache): int foreach ($allKeys as $key) { if (!\in_array($key, $managedKeys, true) && !$this->isSmartCacheInternalKey($key)) { - if ($repository->forget($key)) { + if ($cache->store()->forget($key)) { $cleared++; $this->line("Cleared key: {$key}"); } diff --git a/src/Console/Commands/StatusCommand.php b/src/Console/Commands/StatusCommand.php index 2fff4e6..09ce510 100644 --- a/src/Console/Commands/StatusCommand.php +++ b/src/Console/Commands/StatusCommand.php @@ -123,8 +123,7 @@ protected function displayConfiguration(ConfigRepository $config): void protected function findAllNonManagedKeys(SmartCache $cache): array { - $repository = $cache->repository(); - $store = $repository->getStore(); + $store = $cache->getStore(); $managedKeys = $cache->getManagedKeys(); $nonManagedKeys = []; From fa1ffdd35193c3578fbc68e6a3c0c8e6c8e631f1 Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:45:11 +0400 Subject: [PATCH 7/9] Update console command tests for new API --- tests/Unit/Console/ClearCommandTest.php | 21 ++++++++------------- tests/Unit/Console/StatusCommandTest.php | 22 ++++++---------------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/tests/Unit/Console/ClearCommandTest.php b/tests/Unit/Console/ClearCommandTest.php index b6d07ab..7ba739f 100644 --- a/tests/Unit/Console/ClearCommandTest.php +++ b/tests/Unit/Console/ClearCommandTest.php @@ -63,18 +63,13 @@ public function test_clear_command_with_no_managed_keys_and_force() ->twice() // Called once for display, once in clearOrphanedKeys ->andReturn([]); - // Mock the repository to return a cache repository that will simulate an unsupported driver - $mockRepository = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); + // Mock getStore to return a store that will simulate an unsupported driver $mockStore = Mockery::mock(\stdClass::class); // Use stdClass to simulate unsupported driver - $mockRepository->shouldReceive('getStore') + $this->mockSmartCache->shouldReceive('getStore') ->once() ->andReturn($mockStore); - $this->mockSmartCache->shouldReceive('repository') - ->once() - ->andReturn($mockRepository); - // Inject the mock into the command $this->command->setLaravel($this->app); $this->app->instance(SmartCache::class, $this->mockSmartCache); @@ -302,17 +297,17 @@ public function test_clear_specific_key_not_managed_but_exists_with_force() ->once() ->with($existingKey) ->andReturn(true); - - // Mock the repository() method to return a cache repository - $mockStore = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); - $mockStore->shouldReceive('forget') + + // Mock the store() method to return a SmartCache instance for non-managed key clearing + $mockStoreInstance = Mockery::mock(SmartCache::class); + $mockStoreInstance->shouldReceive('forget') ->once() ->with($existingKey) ->andReturn(true); - $this->mockSmartCache->shouldReceive('repository') + $this->mockSmartCache->shouldReceive('store') ->once() - ->andReturn($mockStore); + ->andReturn($mockStoreInstance); // Inject the mock into the command $this->command->setLaravel($this->app); diff --git a/tests/Unit/Console/StatusCommandTest.php b/tests/Unit/Console/StatusCommandTest.php index 091a9bb..1c3de63 100644 --- a/tests/Unit/Console/StatusCommandTest.php +++ b/tests/Unit/Console/StatusCommandTest.php @@ -300,19 +300,14 @@ public function test_status_command_with_force_option_no_orphaned_keys() ->with('smart-cache') ->once() ->andReturn($this->getDefaultSmartCacheConfig()); - - // Mock the repository() method to return a cache repository - $mockRepository = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); + + // Mock getStore() to return a cache store $mockStore = Mockery::mock(\Illuminate\Cache\ArrayStore::class); - $mockRepository->shouldReceive('getStore') + $this->mockSmartCache->shouldReceive('getStore') ->once() ->andReturn($mockStore); - $this->mockSmartCache->shouldReceive('repository') - ->once() - ->andReturn($mockRepository); - // Mock has() calls to check if managed keys exist $this->mockSmartCache->shouldReceive('has') ->with('key1') @@ -354,19 +349,14 @@ public function test_status_command_with_force_option_finds_missing_managed_keys ->with('smart-cache') ->once() ->andReturn($this->getDefaultSmartCacheConfig()); - - // Mock the repository() method to return a cache repository - $mockRepository = Mockery::mock(\Illuminate\Contracts\Cache\Repository::class); + + // Mock getStore() to return a cache store $mockStore = Mockery::mock(\Illuminate\Cache\ArrayStore::class); - $mockRepository->shouldReceive('getStore') + $this->mockSmartCache->shouldReceive('getStore') ->once() ->andReturn($mockStore); - $this->mockSmartCache->shouldReceive('repository') - ->once() - ->andReturn($mockRepository); - // Mock has() calls - one key exists, two are missing $this->mockSmartCache->shouldReceive('has') ->with('existing-key') From 80d2f952d7e66095c1a56e4a2f72a2ca8fc0878a Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:45:18 +0400 Subject: [PATCH 8/9] Add Repository interface compliance tests --- tests/Unit/SmartCacheTest.php | 184 ++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/tests/Unit/SmartCacheTest.php b/tests/Unit/SmartCacheTest.php index bfc4c6b..4d81bb6 100644 --- a/tests/Unit/SmartCacheTest.php +++ b/tests/Unit/SmartCacheTest.php @@ -629,6 +629,190 @@ public function test_flexible_method_with_optimization_integration() $this->assertNull($this->smartCache->get($metaKey)); } + // ======================================== + // Repository Interface Compliance Tests + // ======================================== + + public function test_smart_cache_implements_repository_interface() + { + $this->assertInstanceOf(\Illuminate\Contracts\Cache\Repository::class, $this->smartCache); + } + + public function test_pull_retrieves_and_deletes_item() + { + $key = 'pull-test-key'; + $value = 'pull-test-value'; + + $this->smartCache->put($key, $value); + $this->assertTrue($this->smartCache->has($key)); + + $pulled = $this->smartCache->pull($key); + $this->assertEquals($value, $pulled); + $this->assertFalse($this->smartCache->has($key)); + } + + public function test_pull_returns_default_when_key_missing() + { + $default = 'default-value'; + $pulled = $this->smartCache->pull('non-existent-key', $default); + $this->assertEquals($default, $pulled); + } + + public function test_add_stores_item_only_if_not_exists() + { + $key = 'add-test-key'; + + // Add should succeed when key doesn't exist + $result = $this->smartCache->add($key, 'first-value'); + $this->assertTrue($result); + $this->assertEquals('first-value', $this->smartCache->get($key)); + + // Add should fail when key already exists + $result = $this->smartCache->add($key, 'second-value'); + $this->assertFalse($result); + $this->assertEquals('first-value', $this->smartCache->get($key)); // Still first value + } + + public function test_increment_increases_value() + { + $key = 'increment-test-key'; + $this->getCacheStore()->put($key, 5); + + $result = $this->smartCache->increment($key); + $this->assertEquals(6, $result); + + $result = $this->smartCache->increment($key, 3); + $this->assertEquals(9, $result); + } + + public function test_decrement_decreases_value() + { + $key = 'decrement-test-key'; + $this->getCacheStore()->put($key, 10); + + $result = $this->smartCache->decrement($key); + $this->assertEquals(9, $result); + + $result = $this->smartCache->decrement($key, 4); + $this->assertEquals(5, $result); + } + + public function test_sear_is_alias_for_remember_forever() + { + $key = 'sear-test-key'; + $callCount = 0; + + $callback = function () use (&$callCount) { + $callCount++; + return 'seared-value'; + }; + + // First call should execute callback + $value = $this->smartCache->sear($key, $callback); + $this->assertEquals('seared-value', $value); + $this->assertEquals(1, $callCount); + + // Second call should return cached value without executing callback + $value = $this->smartCache->sear($key, $callback); + $this->assertEquals('seared-value', $value); + $this->assertEquals(1, $callCount); // Callback should not be called again + } + + public function test_set_is_alias_for_put() + { + $key = 'set-test-key'; + $value = 'set-test-value'; + + $result = $this->smartCache->set($key, $value); + $this->assertTrue($result); + $this->assertEquals($value, $this->smartCache->get($key)); + } + + public function test_delete_is_alias_for_forget() + { + $key = 'delete-test-key'; + $value = 'delete-test-value'; + + $this->smartCache->put($key, $value); + $this->assertTrue($this->smartCache->has($key)); + + $result = $this->smartCache->delete($key); + $this->assertTrue($result); + $this->assertFalse($this->smartCache->has($key)); + } + + public function test_get_store_returns_underlying_store() + { + $store = $this->smartCache->getStore(); + $this->assertInstanceOf(\Illuminate\Contracts\Cache\Store::class, $store); + } + + public function test_get_multiple_retrieves_multiple_items() + { + $this->smartCache->put('multi1', 'value1'); + $this->smartCache->put('multi2', 'value2'); + $this->smartCache->put('multi3', 'value3'); + + $results = $this->smartCache->getMultiple(['multi1', 'multi2', 'multi3']); + + $this->assertIsIterable($results); + $this->assertEquals('value1', $results['multi1']); + $this->assertEquals('value2', $results['multi2']); + $this->assertEquals('value3', $results['multi3']); + } + + public function test_get_multiple_returns_default_for_missing_keys() + { + $this->smartCache->put('existing', 'exists'); + + $results = $this->smartCache->getMultiple(['existing', 'missing'], 'default'); + + $this->assertEquals('exists', $results['existing']); + $this->assertEquals('default', $results['missing']); + } + + public function test_set_multiple_stores_multiple_items() + { + $values = [ + 'setmulti1' => 'val1', + 'setmulti2' => 'val2', + 'setmulti3' => 'val3', + ]; + + $result = $this->smartCache->setMultiple($values); + $this->assertTrue($result); + + $this->assertEquals('val1', $this->smartCache->get('setmulti1')); + $this->assertEquals('val2', $this->smartCache->get('setmulti2')); + $this->assertEquals('val3', $this->smartCache->get('setmulti3')); + } + + public function test_delete_multiple_removes_multiple_items() + { + $this->smartCache->put('del1', 'value1'); + $this->smartCache->put('del2', 'value2'); + $this->smartCache->put('del3', 'value3'); + + $result = $this->smartCache->deleteMultiple(['del1', 'del2']); + $this->assertTrue($result); + + $this->assertFalse($this->smartCache->has('del1')); + $this->assertFalse($this->smartCache->has('del2')); + $this->assertTrue($this->smartCache->has('del3')); // This one was not deleted + } + + public function test_store_returns_repository_compatible_instance() + { + $storeInstance = $this->smartCache->store('array'); + + // Should be both SmartCache and Repository + $this->assertInstanceOf(\SmartCache\SmartCache::class, $storeInstance); + $this->assertInstanceOf(\Illuminate\Contracts\Cache\Repository::class, $storeInstance); + + // Should have getStore() method working + $this->assertInstanceOf(\Illuminate\Contracts\Cache\Store::class, $storeInstance->getStore()); + } + protected function tearDown(): void { Mockery::close(); From c14233f1caf341681158d34d303d65ba075e2094 Mon Sep 17 00:00:00 2001 From: Ismael Azaran Date: Tue, 20 Jan 2026 17:45:26 +0400 Subject: [PATCH 9/9] Update docs for Repository compatibility --- README.md | 2 ++ docs/index.html | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 87197b2..a5e79fb 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,8 @@ SmartCache::store('file')->put('config', $config, 86400); SmartCache::repository('redis')->put('key', $value, 3600); ``` +> **Full Laravel Compatibility:** SmartCache implements Laravel's `Repository` interface, so it works seamlessly with any code that type-hints `Illuminate\Contracts\Cache\Repository`. The `store()` method returns a SmartCache instance that is also a valid Repository. + ## 💡 Core Features (Automatic Optimization) ### 1. Intelligent Compression diff --git a/docs/index.html b/docs/index.html index 4a4c91d..bffa22a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -788,6 +788,10 @@

Using Different Cache Drivers

Note: The store() method returns a SmartCache instance, so all optimization strategies (compression, chunking, encryption, etc.) continue to work. Use repository() if you need direct access to Laravel's cache without SmartCache optimizations.
+
+ Full Laravel Compatibility: SmartCache implements Laravel's Illuminate\Contracts\Cache\Repository interface, so it works seamlessly with any code that type-hints Repository. The store() method returns a SmartCache instance that is also a valid Repository, ensuring zero breaking changes when migrating from Laravel's Cache facade. +
+

Automatic Optimization

SmartCache automatically optimizes your data when beneficial:

@@ -1740,12 +1744,12 @@

🔧 Basic Cache Operations

SmartCache::store() Enhanced!
SmartCache::store(string|null $name = null): static
-

Get a SmartCache instance for a specific cache driver. All SmartCache optimizations (compression, chunking, encryption, etc.) are preserved.

+

Get a SmartCache instance for a specific cache driver. All SmartCache optimizations (compression, chunking, encryption, etc.) are preserved. The returned instance also implements Illuminate\Contracts\Cache\Repository for full Laravel compatibility.

$name (string|null) - The cache store name (redis, file, memcached, etc.)
- Returns: static - SmartCache instance configured for the specified store + Returns: static - SmartCache instance configured for the specified store (also implements Repository)
Examples: @@ -1759,7 +1763,13 @@

🔧 Basic Cache Operations

// Chain operations on a specific store $redisCache = SmartCache::store('redis'); $redisCache->put('users', $users, 3600); -$redisCache->put('products', $products, 3600); +$redisCache->put('products', $products, 3600); + +// Works with Repository type hints +function cacheData(\Illuminate\Contracts\Cache\Repository $cache) { + $cache->put('key', 'value', 3600); +} +cacheData(SmartCache::store('redis')); // ✅ Works!