├── LICENSE ├── README.md ├── UPGRADE.md ├── composer.json ├── config └── cache.php └── src ├── Concerns ├── Arrayable.php ├── Call.php └── Has.php ├── Facades └── Support │ ├── Key.php │ ├── Tag.php │ └── Ttl.php ├── ServiceProvider.php ├── Services ├── Cache.php └── Storages │ ├── Disabled.php │ ├── MainStore.php │ ├── Store.php │ └── TaggedStore.php └── Support ├── CacheManager.php ├── Key.php ├── Tag.php └── Ttl.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2025 Andrey Helldar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Smart Cache for Laravel 2 | 3 | Laravel Cache 4 | 5 | [![Stable Version][badge_stable]][link_packagist] 6 | [![Total Downloads][badge_downloads]][link_packagist] 7 | [![Github Workflow Status][badge_build]][link_build] 8 | [![License][badge_license]][link_license] 9 | 10 | ## Installation 11 | 12 | To get the latest version of `Smart Cache`, simply require the project using [Composer](https://getcomposer.org): 13 | 14 | ```bash 15 | composer require dragon-code/laravel-cache 16 | ``` 17 | 18 | Or manually update `require` block of `composer.json` and run `composer update`. 19 | 20 | ```json 21 | { 22 | "require": { 23 | "dragon-code/laravel-cache": "^4.0" 24 | } 25 | } 26 | ``` 27 | 28 | ## Upgrade Guide 29 | 30 | Information on upgrade from version 3 to 4 is located in the [UPGRADE](UPGRADE.md) file. 31 | 32 | ## Using 33 | 34 | ### Keys And Tags 35 | 36 | In addition to passing an explicit value, you can also pass objects and arrays to the `keys` and `tags` methods. 37 | 38 | For example: 39 | 40 | ```php 41 | use DragonCode\Cache\Services\Cache; 42 | use Tests\Fixtures\Dto\DtoObject; 43 | use Tests\Fixtures\Simple\CustomObject; 44 | 45 | $arr1 = ['foo', 'bar']; 46 | $arr2 = new ArrayObject(['foo', 'bar']); 47 | $arr3 = DtoObject::make(['foo' => 'Foo', 'bar'=> 'Bar']); 48 | $arr4 = new CustomObject(); 49 | 50 | Cache::make()->key($arr1)->tags($arr1); 51 | Cache::make()->key($arr2)->tags($arr3); 52 | Cache::make()->key($arr2)->tags($arr3); 53 | Cache::make()->key($arr4)->tags($arr4); 54 | 55 | Cache::make() 56 | ->key([$arr1, $arr2, $arr3, $arr4, 'foo', 'bar']) 57 | ->tags([$arr1, $arr2, $arr3, $arr4, 'foo', 'bar']); 58 | ``` 59 | 60 | Unpacking and processing of objects occurs as follows: 61 | 62 | ```php 63 | use DragonCode\Cache\Services\Cache; 64 | use Tests\Fixtures\Dto\DtoObject; 65 | use Tests\Fixtures\Simple\CustomObject; 66 | 67 | ['Foo', 'Bar']; 68 | // as key: ['Foo', 'Bar'] 69 | // as tag: ['foo', 'bar'] 70 | 71 | new ArrayObject(['Foo', 'Bar']); 72 | // as key: ['Foo', 'Bar'] 73 | // as tag: ['foo', 'bar'] 74 | 75 | DtoObject::make(['foo' => 'Foo', 'bar'=> 'Bar']); 76 | // as key: ['Foo', 'Bar'] 77 | // as tag: ['foo', 'bar'] 78 | 79 | new CustomObject(); 80 | // as key: ['Foo'] 81 | // as tag: ['foo'] 82 | ``` 83 | 84 | #### Keys Handling 85 | 86 | Since the main problem of working with the cache's key compilation, this package solves it. 87 | 88 | By passing values to the `keys` method, we get a ready-made key at the output. 89 | 90 | The hash is formed by the value `key=value`, which allows avoiding collisions when passing identical objects. 91 | 92 | In the case of passing nested arrays, the key is formed according to the principle `key1.key2=value`, where `key1` 93 | and `key2` are the keys of each nested array. 94 | 95 | For example: 96 | 97 | ```php 98 | use DragonCode\Cache\Services\Cache; 99 | 100 | Cache::make()->key('foo', 'bar', [null, 'baz', 'baq']); 101 | 102 | // Key is `d76f2bde023f5602ae837d01f4ec1876:660a13c00e04c0d3ffb4dbf02a84a07a:6fc3659bd986e86534c6587caf5f431a:bd62cbee62e027d0be4b1656781edcbf` 103 | ``` 104 | 105 | This means that when writing to the cache, the tree view will be used. 106 | 107 | For example: 108 | 109 | ```php 110 | use DragonCode\Cache\Services\Cache; 111 | 112 | Cache::make()->key('foo', 'foo')->put('Foo'); 113 | Cache::make()->key('foo', 'bar')->put('Bar'); 114 | Cache::make()->key('baz')->put('Baz'); 115 | 116 | // d76f2bde023f5602ae837d01f4ec1876: 117 | // 086f76c144511e1198c29a261e87ca50: Foo 118 | // 660a13c00e04c0d3ffb4dbf02a84a07a: Bar 119 | // 1b9829f3bd21835a15735f3a65cc75e9: Baz 120 | ``` 121 | 122 | #### Disable key hashing 123 | 124 | In some cases, you need to disable the use of the key hashing mechanism. 125 | To do this, simply call the `hashKey(false)` method: 126 | 127 | ```php 128 | use DragonCode\Cache\Services\Cache; 129 | 130 | Cache::make()->key('foo', 'foo')->hashKey(false)->put('Foo'); 131 | Cache::make()->key('foo', 'bar')->hashKey(false)->put('Bar'); 132 | Cache::make()->key('baz')->hashKey(false)->put('Baz'); 133 | 134 | // 0=foo: 135 | // 1=foo: Foo 136 | // 1=bar: Bar 137 | // 0=baz: Baz 138 | ``` 139 | 140 | ```php 141 | use DragonCode\Cache\Services\Cache; 142 | 143 | Cache::make()->key([ 144 | ['foo' => 'Foo'], 145 | ['bar' => 'Bar'], 146 | [['Baz', 'Qwerty']], 147 | ])->hashKey(false)->put('Baz'); 148 | 149 | // 0.foo=Foo:1.bar=Bar:2.0.0=Baz:2.0.1=Qwerty 150 | ``` 151 | 152 | ### With Authentication 153 | 154 | In some cases, it is necessary to bind the cache to certain users. To do this, we have added the `withAuth` helper. 155 | 156 | ```php 157 | use DragonCode\Cache\Services\Cache; 158 | use Illuminate\Support\Facades\Auth; 159 | 160 | Cache::make()->withAuth()->key('foo', 'bar'); 161 | 162 | // instead of 163 | Cache::make()->key(get_class(Auth::user()), Auth::id(), 'foo', 'bar'); 164 | ``` 165 | 166 | When processing requests with a call to the withAuth method, the binding will be carried out not only by identifier, but 167 | also by reference to the model class, since a project can 168 | have several models with the possibility of authorization. 169 | 170 | For example, `App\Models\Employee`, `App\Models\User`. 171 | 172 | ### When Enabled 173 | 174 | #### Basic 175 | 176 | ```php 177 | use DragonCode\Cache\Services\Cache; 178 | 179 | $cache = Cache::make() 180 | ->key('foo', 'bar', ['baz', 'baq']) 181 | ->ttl(200, true); 182 | // When `true` is equal to 200 minutes. 183 | // When `false` is equal to 200 seconds. 184 | // By default, `true` 185 | 186 | $cache->put(static fn() => 'Some value'); 187 | // or 188 | $cache->put('Some value'); 189 | // Contains cached `Some value` 190 | 191 | $cache->remember(static fn() => 'Some value'); 192 | // or 193 | $cache->remember('Some value'); 194 | // Contains cached `Some value` 195 | 196 | $cache->rememberForever(static fn() => 'Some value'); 197 | // or 198 | $cache->rememberForever('Some value'); 199 | // Contains cached `Some value` 200 | 201 | // Uses the functionality of the `Cache::flexible()` method 202 | $cache->flexible(50)->remember('Some value'); 203 | // equals `Cache::flexible($key, [50, 200], fn () => 'Some value')` 204 | 205 | $cache->flexible(-50)->remember('Some value'); 206 | // equals `Cache::flexible($key, [150, 200], fn () => 'Some value')` 207 | 208 | $cache->flexible(0)->remember('Some value'); // By default, `0` 209 | // equals `Cache::flexible($key, [170, 200], fn () => 'Some value')` 210 | // (200 - 15%) = 170 211 | 212 | $cache->flexible(50, true); // 50 minutes 213 | $cache->flexible(50, false); // 50 seconds 214 | $cache->flexible(0, false); // the `true/false` modifier is not used 215 | 216 | $cache->get(); 217 | // Returns cached `Some value` 218 | 219 | $cache->has(); 220 | // Returns `true` 221 | 222 | $cache->doesntHave(); 223 | // Returns `false` 224 | 225 | $cache->forget(); 226 | // Will remove the key from the cache. 227 | 228 | $cache->flush(); 229 | // Clears keys or tags by value 230 | ``` 231 | 232 | ```php 233 | use DragonCode\Cache\Services\Cache; 234 | use App\Models\User; 235 | 236 | $user = User::first(); 237 | 238 | $cache = Cache::make()->key('foo'); 239 | 240 | $cache->put(static fn() => $user); 241 | // or 242 | $cache->put($user); 243 | // Contains cached `$user` 244 | 245 | $cache->remember(static fn() => $user); 246 | // or 247 | $cache->remember($user); 248 | // Contains cached `$user` 249 | 250 | $cache->rememberForever(static fn() => $user); 251 | // or 252 | $cache->rememberForever($user); 253 | // Contains cached `$user` 254 | 255 | $cache->flexible()->remember($user); 256 | // Returns User model with flexibility 257 | 258 | $cache->get(); 259 | // Returns User model 260 | 261 | $cache->has(); 262 | // Returns `true` 263 | 264 | $cache->doesntHave(); 265 | // Returns `false` 266 | 267 | $cache->forget(); 268 | // Will remove the key from the cache. 269 | 270 | $cache->flush(); 271 | // Clears keys or tags by value 272 | ``` 273 | 274 | #### Method Call Chain 275 | 276 | Sometimes in the process of working with a cache, it becomes necessary to call some code between certain actions, and in 277 | this case the `call` method will come to the rescue: 278 | 279 | ```php 280 | use DragonCode\Cache\Services\Cache; 281 | 282 | $cache = Cache::make()->key('foo'); 283 | $warmUp = false; 284 | 285 | $cache 286 | ->call(fn (Cache $cache) => $cache->forget(), $warmUp) 287 | ->call(fn () => $someService->someMethod()) 288 | ->remember('foo'); 289 | ``` 290 | 291 | In addition, the `forget` method now returns an instance of the `Cache` object, so it can be used like this: 292 | 293 | ```php 294 | use DragonCode\Cache\Services\Cache; 295 | 296 | $cache = Cache::make()->key('foo'); 297 | 298 | $cache 299 | ->forget() 300 | ->call(fn () => $someService->someMethod()) 301 | ->remember('foo'); 302 | ``` 303 | 304 | Previously, you had to use the following sequence: 305 | 306 | ```php 307 | use DragonCode\Cache\Services\Cache; 308 | 309 | $cache = Cache::make()->key('foo'); 310 | $warmUp = false; 311 | 312 | if ($warmUp) { 313 | $cache->forget(); 314 | } 315 | 316 | $someService->someMethod() 317 | 318 | $cache->remember('foo'); 319 | ``` 320 | 321 | #### Custom TTL 322 | 323 | By default, the cache will be written for 1 day. 324 | 325 | The cache will be written for the specified number of minutes, seconds or the `DateTimeInterface` instance. 326 | 327 | It does not matter in which direction the time shift will be. During processing, the value is converted to the `abs()`. 328 | 329 | ##### As Minutes 330 | 331 | ```php 332 | use Carbon\Carbon; 333 | use DateTime; 334 | use DragonCode\Cache\Services\Cache; 335 | use DragonCode\Cache\Support\Ttl; 336 | 337 | Cache::make()->ttl(10); 338 | Cache::make()->ttl('10'); 339 | Cache::make()->ttl(fn () => 10); 340 | 341 | Cache::make()->ttl(Carbon::now()->addDay()); 342 | Cache::make()->ttl(new DateTime('tomorrow')); 343 | ``` 344 | 345 | ##### As Seconds 346 | 347 | ```php 348 | use Carbon\Carbon; 349 | use DateTime; 350 | use DragonCode\Cache\Services\Cache; 351 | 352 | Cache::make()->ttl(10, false); 353 | Cache::make()->ttl('10', false); 354 | Cache::make()->ttl(fn () => 10, false); 355 | 356 | Cache::make()->ttl(Carbon::now()->addDay(), false); 357 | Cache::make()->ttl(new DateTime('tomorrow'), false); 358 | ``` 359 | 360 | ##### By Objects And Custom Strings 361 | 362 | You can also store all TTL values in one place - in the `config/cache.php` file. 363 | 364 | To do this, add a `ttl` block to the file and [`define`](config/cache.php) a TTL for the objects. 365 | 366 | After that you can use the following construction: 367 | 368 | ```php 369 | use DragonCode\Cache\Services\Cache; 370 | use Tests\Fixtures\Simple\CustomObject; 371 | 372 | Cache::make()->ttl(CustomObject::class); 373 | Cache::make()->ttl(new CustomObject()); 374 | Cache::make()->ttl('custom_key'); 375 | Cache::make()->ttl((object) ['foo' => 'Foo']); 376 | 377 | // You can also specify that these values are in seconds, not minutes: 378 | Cache::make()->ttl(CustomObject::class, false); 379 | Cache::make()->ttl(new CustomObject(), false); 380 | Cache::make()->ttl('custom_key', false); 381 | Cache::make()->ttl((object) ['foo' => 'Foo'], false); 382 | ``` 383 | 384 | If the value is not found, the [default value](config/cache.php) will be taken, which you can also override in 385 | the [configuration file](config/cache.php). 386 | 387 | #### Tagged 388 | 389 | For repositories that support tagging, the keys will be saved separated by tags. 390 | 391 | ```php 392 | use DragonCode\Cache\Services\Cache; 393 | 394 | $cache = Cache::make() 395 | ->tags('actor', 'author') 396 | ->key('foo', 'bar', ['baz', 'baq']); 397 | 398 | $cache->put(static fn() => 'Some value'); 399 | // or 400 | $cache->put('Some value'); 401 | // Contains cached `Some value` 402 | 403 | $cache->get(); 404 | // Returns cached `Some value` 405 | 406 | $cache->has(); 407 | // Returns `true` 408 | 409 | $cache->doesntHave(); 410 | // Returns `false` 411 | 412 | $cache->forget(); 413 | // Will remove the key from the cache. 414 | 415 | $cache->flush(); 416 | // Clears keys or tags by value 417 | ``` 418 | 419 | To retrieve a tagged cache item, pass the same ordered list of tags to the tags method and then call the get method with 420 | the key you wish to retrieve: 421 | 422 | ```php 423 | use DragonCode\Cache\Services\Cache; 424 | 425 | $cache = Cache::make()->key('foo', 'bar'); 426 | 427 | $cache->tags('actor', 'author')->put(static fn() => 'Some value'); 428 | // or 429 | $cache->tags('actor', 'author')->put('Some value'); 430 | // Contains cached `Some value` 431 | 432 | $cache->tags('actor', 'author')->get(); 433 | // Returns cached `Some value` 434 | 435 | $cache->tags('actor')->get(); 436 | // Returns `null` 437 | 438 | $cache->tags('author')->get(); 439 | // Returns `null` 440 | 441 | $cache->tags('author')->flush(); 442 | // Clears keys or tags by value 443 | ``` 444 | 445 | > See the official Laravel [documentation](https://laravel.com/docs/cache#accessing-tagged-cache-items). 446 | 447 | ### When Disabled 448 | 449 | Passing `when = false` will not write to the cache. 450 | 451 | ```php 452 | use DragonCode\Cache\Services\Cache; 453 | 454 | $cache = Cache::make() 455 | ->when(false) 456 | ->key('foo', 'bar'); 457 | 458 | $value = $cache->put(static fn() => 'Some value'); 459 | // or 460 | $value = $cache->put('Some value'); 461 | // Returns `Some value` 462 | 463 | $cache->get(); 464 | // Returns `null` 465 | 466 | $cache->has(); 467 | // Returns `false` 468 | 469 | $cache->doesntHave(); 470 | // Returns `true` 471 | ``` 472 | 473 | You can also define whether to enable or disable the use of cache storage in the settings. 474 | 475 | For example: 476 | 477 | ```php 478 | // config/cache.php 479 | return [ 480 | 'enabled' => [ 481 | // App\Models\Page::class => true, 482 | // 483 | // 'stdClass' => false, 484 | // 485 | // 'foo' => false, 486 | ], 487 | ]; 488 | ``` 489 | 490 | ```php 491 | use App\Services\Some;use DragonCode\Cache\Services\Cache; 492 | 493 | // as string 494 | $cache = Cache::make()->when('foo'); 495 | 496 | // as class-string 497 | $cache = Cache::make()->when(Some::class); 498 | $cache = Cache::make()->when(static::class); 499 | $cache = Cache::make()->when(self::class); 500 | 501 | // as class 502 | $cache = Cache::make()->when(new Some); 503 | $cache = Cache::make()->when($this); 504 | 505 | // as stdClass 506 | $cache = Cache::make()->when((object)['foo' => 'Foo']); 507 | ``` 508 | 509 | ## License 510 | 511 | This package's licensed under the [MIT License](LICENSE). 512 | 513 | 514 | [badge_build]: https://img.shields.io/github/actions/workflow/status/TheDragonCode/laravel-cache/phpunit.yml?style=flat-square 515 | 516 | [badge_downloads]: https://img.shields.io/packagist/dt/dragon-code/laravel-cache.svg?style=flat-square 517 | 518 | [badge_license]: https://img.shields.io/github/license/TheDragonCode/laravel-cache.svg?style=flat-square 519 | 520 | [badge_stable]: https://img.shields.io/github/v/release/TheDragonCode/laravel-cache?label=stable&style=flat-square 521 | 522 | [link_build]: https://github.com/TheDragonCode/laravel-cache/actions 523 | 524 | [link_license]: LICENSE 525 | 526 | [link_packagist]: https://packagist.org/packages/dragon-code/laravel-cache 527 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrade to 4.0 from 3.12 2 | 3 | ## High Impact Changes 4 | 5 | ### PHP 8.2 Required 6 | 7 | `Laravel Cache` now required PHP 8.2.0 or greater. 8 | 9 | ### Laravel 11+ Required 10 | 11 | `Laravel Cache` now required Laravel Framework 11 or greater. 12 | 13 | ### Composer Dependencies 14 | 15 | You should update the following dependency in your application's composer.json file: 16 | 17 | - `dragon-code/laravel-cache` to `^4.0` 18 | 19 | The support of the following dependencies has been removed: 20 | 21 | - `dragon-code/contracts` 22 | - `dragon-code/simple-dto` 23 | 24 | ### Application Structure 25 | 26 | The use of the dependence of the contracts `dragon-code/contracts` was removed. 27 | 28 | This means that the `DragonCode\Contracts\*` contracts will no longer be working. 29 | 30 | ### Replaces Namespaces 31 | 32 | - `DragonCode\Contracts\Support\Arrayable` replace with `Illuminate\Contracts\Support\Arrayable` 33 | 34 | ## Low Impact Changes 35 | 36 | ### TTL Time Constants 37 | 38 | Constants `DAY`, `MONTH` and `WEEK` was deleted from `DragonCode\Cache\Support\Ttl` class. 39 | 40 | ### Properties 41 | 42 | 1. The case of the properties of the properties from `snake_case` with `camelCase` is changed. 43 | 2. The `hashing_key` property of `DragonCode\Cache\Services\Cache` class was renamed with `useHash`. 44 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dragon-code/laravel-cache", 3 | "description": "An improved interface for working with cache", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "laravel", 8 | "cache", 9 | "helper", 10 | "dragon-code", 11 | "dragon", 12 | "andrey-helldar" 13 | ], 14 | "authors": [ 15 | { 16 | "name": "Andrey Helldar", 17 | "email": "helldar@dragon-code.pro", 18 | "homepage": "https://dragon-code.pro" 19 | } 20 | ], 21 | "support": { 22 | "issues": "https://github.com/TheDragonCode/laravel-cache/issues", 23 | "source": "https://github.com/TheDragonCode/laravel-cache" 24 | }, 25 | "funding": [ 26 | { 27 | "type": "boosty", 28 | "url": "https://boosty.to/dragon-code" 29 | }, 30 | { 31 | "type": "yoomoney", 32 | "url": "https://yoomoney.ru/to/410012608840929" 33 | } 34 | ], 35 | "require": { 36 | "php": "^8.2", 37 | "dragon-code/support": "^6.11.3", 38 | "illuminate/contracts": "^11.23 || ^12.0", 39 | "illuminate/support": "^11.23 || ^12.0" 40 | }, 41 | "require-dev": { 42 | "nesbot/carbon": "^2.62 || ^3.0", 43 | "orchestra/testbench": "^9.0 || ^10.0", 44 | "phpunit/phpunit": "^11.0 || ^12.0" 45 | }, 46 | "conflict": { 47 | "andrey-helldar/cache": "*" 48 | }, 49 | "minimum-stability": "stable", 50 | "prefer-stable": true, 51 | "autoload": { 52 | "psr-4": { 53 | "DragonCode\\Cache\\": "src/" 54 | } 55 | }, 56 | "autoload-dev": { 57 | "psr-4": { 58 | "Tests\\": "tests/" 59 | } 60 | }, 61 | "config": { 62 | "allow-plugins": { 63 | "dragon-code/codestyler": true, 64 | "ergebnis/composer-normalize": true, 65 | "friendsofphp/php-cs-fixer": true, 66 | "symfony/thanks": true 67 | }, 68 | "preferred-install": "dist", 69 | "sort-packages": true 70 | }, 71 | "extra": { 72 | "laravel": { 73 | "providers": [ 74 | "DragonCode\\Cache\\ServiceProvider" 75 | ] 76 | } 77 | }, 78 | "scripts": { 79 | "test": "php vendor/bin/phpunit" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | 3600, 7 | 8 | 'ttl' => [ 9 | // App\Models\Page::class => 86400, 10 | // App\Models\News::class => 3600, 11 | // App\Services\Custom::class => 1800, 12 | // 13 | // 'stdClass' => 600, 14 | // 15 | // 'foo' => 300, 16 | // 'bar' => 600, 17 | ], 18 | 19 | 'enabled' => [ 20 | // App\Models\Page::class => true, 21 | // App\Models\News::class => true, 22 | // App\Services\Custom::class => true, 23 | // 24 | // 'stdClass' => false, 25 | // 26 | // 'foo' => false, 27 | // 'bar' => false, 28 | ], 29 | ]; 30 | -------------------------------------------------------------------------------- /src/Concerns/Arrayable.php: -------------------------------------------------------------------------------- 1 | map(function (mixed $value) { 34 | if ($this->isArrayable($value)) { 35 | return $this->toArray($value); 36 | } 37 | 38 | if (is_object($value)) { 39 | return get_class($value); 40 | } 41 | 42 | return $value; 43 | }) 44 | ->flatten() 45 | ->filter(static fn ($value) => ! empty($value) || is_numeric($value) || is_bool($value)) 46 | ->map($callback) 47 | ->values() 48 | ->toArray(); 49 | } 50 | 51 | protected function arrayFlattenKeysMap(array $values, callable $callback): array 52 | { 53 | return Arr::of($values) 54 | ->flattenKeys() 55 | ->filter(static fn ($value) => ! empty($value) || is_numeric($value) || is_bool($value)) 56 | ->map(static fn (mixed $value, mixed $key) => $callback($key . '=' . $value)) 57 | ->toArray(); 58 | } 59 | 60 | protected function flattenKeys(mixed $array, string $delimiter = '.', ?string $prefix = null): array 61 | { 62 | $result = []; 63 | 64 | foreach ($array as $key => $value) { 65 | $new_key = ! empty($prefix) ? $prefix . $delimiter . $key : $key; 66 | 67 | if (is_array($value)) { 68 | $values = $this->flattenKeys($value, $delimiter, $new_key); 69 | 70 | $result = array_merge($result, $values); 71 | 72 | continue; 73 | } 74 | 75 | $result[$new_key] = $value; 76 | } 77 | 78 | return $result; 79 | } 80 | 81 | protected function toArray($value): array 82 | { 83 | return Arr::of(Arr::wrap($value)) 84 | ->map(static fn ($value) => Instance::of($value, Carbon::class) ? $value->toIso8601String() : $value) 85 | ->map(static fn ($value) => Instance::of($value, FormRequest::class) ? $value->validated() : $value) 86 | ->map( 87 | static fn ($value) => Instance::of($value, BackedEnum::class) ? ($value->value ?? $value->name) : $value 88 | ) 89 | ->map(static fn ($value) => is_object($value) ? (Arr::resolve($value) ?: get_class($value)) : $value) 90 | ->resolve() 91 | ->toArray(); 92 | } 93 | 94 | protected function isArrayable($value): bool 95 | { 96 | if (is_array($value)) { 97 | return true; 98 | } 99 | 100 | if ( 101 | is_string($value) 102 | && method_exists($value, 'toArray') 103 | && ! Reflection::isStaticMethod($value, 'toArray') 104 | ) { 105 | return false; 106 | } 107 | 108 | if ( 109 | Instance::of($value, [ 110 | ArrayableHelper::class, 111 | IlluminateArrayable::class, 112 | ArrayObject::class, 113 | ArrayAccess::class, 114 | ]) 115 | ) { 116 | return true; 117 | } 118 | 119 | return Instance::of($value, Closure::class) && method_exists($value, 'toArray'); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Concerns/Call.php: -------------------------------------------------------------------------------- 1 | isFunction($callback) ? $callback() : $callback; 18 | } 19 | 20 | protected function makeCallable(mixed $value): callable 21 | { 22 | if (is_callable($value) && ! $this->isFunction($value)) { 23 | return $value; 24 | } 25 | 26 | return static fn () => $value; 27 | } 28 | 29 | protected function isFunction($value): bool 30 | { 31 | return is_string($value) && function_exists($value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Concerns/Has.php: -------------------------------------------------------------------------------- 1 | publishes([ 14 | __DIR__ . '/../config/cache.php' => $this->app->configPath('cache.php'), 15 | ], 'config'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Services/Cache.php: -------------------------------------------------------------------------------- 1 | when = match (true) { 48 | is_string($when) => config('cache.enabled.' . $when, true), 49 | is_object($when) => config('cache.enabled.' . get_class($when), true), 50 | default => $when 51 | }; 52 | 53 | return $this; 54 | } 55 | 56 | public function ttl($value, bool $isMinutes = true): Cache 57 | { 58 | $this->ttl = $isMinutes 59 | ? Ttl::fromMinutes($value) 60 | : Ttl::fromSeconds($value); 61 | 62 | return $this; 63 | } 64 | 65 | public function tags(string ...$tags): Cache 66 | { 67 | $this->tags = Tag::get($tags); 68 | 69 | return $this; 70 | } 71 | 72 | public function withAuth(): Cache 73 | { 74 | $this->auth = Auth::check() ? [get_class(Auth::user()), Auth::id()] : 'guest'; 75 | 76 | return $this; 77 | } 78 | 79 | public function hashKey(bool $hash = true): Cache 80 | { 81 | $this->useHash = $hash; 82 | 83 | return $this; 84 | } 85 | 86 | public function key(...$values): Cache 87 | { 88 | $this->key = $values; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * It sets the time for stale of the key for its background update. 95 | * 96 | * > Note: 97 | * > 98 | * > This method works only in tandem with the `remember()` method. 99 | * 100 | * A positive value indicates the lifetime of the key before it stale. 101 | * For example, 102 | * ``` 103 | * Cache::flexible('some', [60, 300], fn () => ...) 104 | * ``` 105 | * 106 | * A negative value indicates the time the key will expire. 107 | * For example, 108 | * ``` 109 | * Cache::flexible('some', [300 - 60, 300], fn () => ...) 110 | * ``` 111 | * 112 | * If you specify 0, then 15% of the remaining time will be taken as the interval. 113 | * For example, 114 | * ``` 115 | * Cache::flexible('some', [(300 - 15% = 255), 300], fn () => ...) 116 | * ``` 117 | * 118 | * By default, `0`. 119 | * 120 | * @see https://laravel.com/docs/12.x/cache#swr 121 | * 122 | * @return $this 123 | */ 124 | public function flexible(int $interval = 0, bool $isMinutes = true): Cache 125 | { 126 | $this->ttlInterval = $isMinutes ? $interval * 60 : $interval; 127 | 128 | return $this; 129 | } 130 | 131 | public function get(): mixed 132 | { 133 | return $this->manager()->get($this->getKey()); 134 | } 135 | 136 | public function put(mixed $value): mixed 137 | { 138 | return $this->manager()->put($this->getKey(), $value, $this->ttl); 139 | } 140 | 141 | public function remember(mixed $value): mixed 142 | { 143 | return $this->ttlInterval === null 144 | ? $this->manager()->remember($this->getKey(), $value, $this->ttl) 145 | : $this->manager()->flexible($this->getKey(), $value, $this->ttl, $this->ttlInterval); 146 | } 147 | 148 | public function rememberForever(mixed $value): mixed 149 | { 150 | return $this->manager()->rememberForever($this->getKey(), $value); 151 | } 152 | 153 | public function forget(): static 154 | { 155 | $this->manager()->forget($this->getKey()); 156 | 157 | return $this; 158 | } 159 | 160 | public function flush(): static 161 | { 162 | $this->manager()->flush(); 163 | 164 | return $this; 165 | } 166 | 167 | public function has(): bool 168 | { 169 | return $this->manager()->has($this->getKey()); 170 | } 171 | 172 | public function doesntHave(): bool 173 | { 174 | return $this->manager()->doesntHave($this->getKey()); 175 | } 176 | 177 | public function call(Closure $callback, mixed $when = true): static 178 | { 179 | if (Call::value($when)) { 180 | Call::value($callback, $this); 181 | } 182 | 183 | return $this; 184 | } 185 | 186 | protected function manager(): CacheManager 187 | { 188 | return CacheManager::make($this->when) 189 | ->tags($this->tags); 190 | } 191 | 192 | protected function getKey(): string 193 | { 194 | if (! empty($this->keyHash)) { 195 | return $this->keyHash; 196 | } 197 | 198 | $key = $this->key; 199 | 200 | if ($this->auth) { 201 | array_unshift($key, $this->auth); 202 | } 203 | 204 | return $this->keyHash = Key::get(':', $key, $this->useHash); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/Services/Storages/Disabled.php: -------------------------------------------------------------------------------- 1 | call($default); 12 | } 13 | 14 | public function put(string $key, $value, int $seconds): mixed 15 | { 16 | return $this->get($key, $value); 17 | } 18 | 19 | public function flexible(string $key, $value, int $seconds, int $interval): mixed 20 | { 21 | return $this->get($key, $value); 22 | } 23 | 24 | public function remember(string $key, $value, int $seconds): mixed 25 | { 26 | return $this->get($key, $value); 27 | } 28 | 29 | public function rememberForever(string $key, $value): mixed 30 | { 31 | return $this->get($key, $value); 32 | } 33 | 34 | public function forget(string $key): void {} 35 | 36 | public function has(string $key): bool 37 | { 38 | return false; 39 | } 40 | 41 | public function flush(): void {} 42 | } 43 | -------------------------------------------------------------------------------- /src/Services/Storages/MainStore.php: -------------------------------------------------------------------------------- 1 | has($key)) { 14 | return Cache::get($key); 15 | } 16 | 17 | return $default; 18 | } 19 | 20 | public function put(string $key, $value, int $seconds): mixed 21 | { 22 | $value = $this->call($value); 23 | 24 | Cache::put($key, $value, $seconds); 25 | 26 | return $value; 27 | } 28 | 29 | public function flexible(string $key, $value, int $seconds, int $interval): mixed 30 | { 31 | $value = $this->makeCallable($value); 32 | 33 | return Cache::flexible($key, [$seconds, $interval], $value); 34 | } 35 | 36 | public function remember(string $key, $value, int $seconds): mixed 37 | { 38 | $value = $this->makeCallable($value); 39 | 40 | return Cache::remember($key, $seconds, $value); 41 | } 42 | 43 | public function rememberForever(string $key, $value): mixed 44 | { 45 | $value = $this->makeCallable($value); 46 | 47 | return Cache::rememberForever($key, $value); 48 | } 49 | 50 | public function forget(string $key): void 51 | { 52 | Cache::forget($key); 53 | } 54 | 55 | public function has(string $key): bool 56 | { 57 | return Cache::has($key); 58 | } 59 | 60 | public function flush(): void 61 | { 62 | Cache::flush(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Services/Storages/Store.php: -------------------------------------------------------------------------------- 1 | has($key); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Services/Storages/TaggedStore.php: -------------------------------------------------------------------------------- 1 | tags = $tags; 17 | 18 | return $this; 19 | } 20 | 21 | public function get(string $key, $default = null): mixed 22 | { 23 | return $this->cache()->get($key) 24 | ?? $this->call($default); 25 | } 26 | 27 | public function put(string $key, $value, int $seconds): mixed 28 | { 29 | $value = $this->call($value); 30 | 31 | $this->cache()->put($key, $value, $seconds); 32 | 33 | return $value; 34 | } 35 | 36 | public function flexible(string $key, $value, int $seconds, int $interval): mixed 37 | { 38 | return $this->cache()->flexible($key, [$interval, $seconds], $this->makeCallable($value)); 39 | } 40 | 41 | public function remember(string $key, $value, int $seconds): mixed 42 | { 43 | return $this->cache()->remember($key, $seconds, $this->makeCallable($value)); 44 | } 45 | 46 | public function rememberForever(string $key, $value): mixed 47 | { 48 | return $this->cache()->rememberForever($key, $this->makeCallable($value)); 49 | } 50 | 51 | public function forget(string $key): void 52 | { 53 | $this->cache()->forget($key); 54 | } 55 | 56 | public function has(string $key): bool 57 | { 58 | return $this->cache()->has($key); 59 | } 60 | 61 | public function flush(): void 62 | { 63 | $this->cache()->flush(); 64 | } 65 | 66 | protected function cache(): TaggedCache 67 | { 68 | return Cache::tags($this->tags); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Support/CacheManager.php: -------------------------------------------------------------------------------- 1 | tags = $tags; 32 | 33 | return $this; 34 | } 35 | 36 | public function get(string $key, $default = null): mixed 37 | { 38 | return $this->instance()->get($key, $default); 39 | } 40 | 41 | public function put(string $key, $value, int $seconds): mixed 42 | { 43 | return $this->instance()->put($key, $value, $seconds); 44 | } 45 | 46 | public function flexible(string $key, $value, int $seconds, int $interval): mixed 47 | { 48 | if ($interval < 0) { 49 | $interval = $seconds - $interval; 50 | } 51 | elseif ($interval === 0) { 52 | $interval = (int) (300 * 0.85); 53 | } 54 | 55 | return $this->instance()->flexible($key, $value, $seconds, $interval); 56 | } 57 | 58 | public function remember(string $key, $value, int $seconds): mixed 59 | { 60 | return $this->instance()->remember($key, $value, $seconds); 61 | } 62 | 63 | public function rememberForever(string $key, $value): mixed 64 | { 65 | return $this->instance()->rememberForever($key, $value); 66 | } 67 | 68 | public function forget(string $key): void 69 | { 70 | $this->instance()->forget($key); 71 | } 72 | 73 | public function has(string $key): bool 74 | { 75 | return $this->instance()->has($key); 76 | } 77 | 78 | public function flush(): void 79 | { 80 | $this->instance()->flush(); 81 | } 82 | 83 | protected function instance(): Store 84 | { 85 | return match (true) { 86 | $this->isDisabled() => Disabled::make(), 87 | $this->allowTags() => TaggedStore::make()->tags($this->tags), 88 | default => MainStore::make(), 89 | }; 90 | } 91 | 92 | protected function isDisabled(): bool 93 | { 94 | return ! $this->when; 95 | } 96 | 97 | protected function allowTags(): bool 98 | { 99 | return ! empty($this->tags) && method_exists(Cache::getStore(), 'tags'); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Support/Key.php: -------------------------------------------------------------------------------- 1 | toArray($values); 24 | 25 | $hashed = $this->hash($values, $hash); 26 | 27 | return implode($separator, $hashed); 28 | } 29 | 30 | protected function hash(array $values, bool $hash = true): array 31 | { 32 | return $this->arrayFlattenKeysMap($values, static fn (mixed $value) => $hash ? hash('xxh128', $value) : $value); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Support/Tag.php: -------------------------------------------------------------------------------- 1 | map( 21 | $this->toArray($tags) 22 | ); 23 | } 24 | 25 | protected function map(array $tags): array 26 | { 27 | return $this->arrayMap($tags, fn (string $tag) => $this->slug($tag)); 28 | } 29 | 30 | protected function slug(string $tag): string 31 | { 32 | return Str::of($tag)->trim()->slug()->toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Support/Ttl.php: -------------------------------------------------------------------------------- 1 | get($minutes, 60); 26 | } 27 | 28 | public function fromSeconds($seconds): int 29 | { 30 | return $this->get($seconds); 31 | } 32 | 33 | public function fromDateTime(DateTimeInterface $dateTime): int 34 | { 35 | $seconds = Carbon::now()->diffInSeconds($dateTime); 36 | 37 | return $this->correct($seconds); 38 | } 39 | 40 | protected function get($value, int $multiplier = 1): int 41 | { 42 | if ($this->hasDateTime($value)) { 43 | return $this->fromDateTime($value); 44 | } 45 | 46 | if ($config = $this->fromConfig($value)) { 47 | return $this->correct($config, $multiplier); 48 | } 49 | 50 | if ($this->hasClosure($value)) { 51 | $value = $this->call($value); 52 | } 53 | 54 | if ($this->hasObject($value)) { 55 | $value = $this->defaultTtl(); 56 | } 57 | 58 | return $this->correct($value, $multiplier); 59 | } 60 | 61 | protected function correct($value, int $multiplier = 1): int 62 | { 63 | $value = (int) $value ?: $this->defaultTtl(); 64 | 65 | return abs($value) * $multiplier; 66 | } 67 | 68 | protected function fromConfig($value): ?int 69 | { 70 | $value = $this->resolveClass($value); 71 | 72 | return $this->configTtl($value); 73 | } 74 | 75 | protected function resolveClass($value): string 76 | { 77 | return $this->hasObject($value) ? get_class($value) : (string) $value; 78 | } 79 | 80 | protected function configTtl(string $value): ?int 81 | { 82 | return config('cache.ttl.' . $value); 83 | } 84 | 85 | protected function defaultTtl(): int 86 | { 87 | return config('cache.ttl_default', $this->default); 88 | } 89 | } 90 | --------------------------------------------------------------------------------