├── .gitattributes ├── .github └── workflows │ └── run-tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── config.php ├── phpunit.xml ├── src ├── Actions │ ├── DeleteCacheAction.php │ ├── DeleteGroupCacheAction.php │ ├── Support │ │ └── UpdateDeleteCache.php │ ├── UpdateCacheAction.php │ └── UpdateGroupCacheAction.php ├── Cache.php ├── CacheEntity.php ├── Commands │ ├── DeleteCacheCommand.php │ ├── DeleteGroupCacheCommand.php │ ├── UpdateCacheCommand.php │ └── UpdateGroupCacheCommand.php ├── DTOs │ ├── CacheData.php │ ├── CacheEvent.php │ ├── CacheStatus.php │ └── CommandData.php ├── Exceptions │ ├── CacheEntityDoesNotExist.php │ ├── CacheGroupNotExist.php │ ├── CacheGroupValueIsNotValid.php │ ├── EntityIsNotAllowed.php │ ├── ModelDoesntUseLaraCacheTrait.php │ └── ModelDoestNotExist.php ├── Facades │ └── LaraCache.php ├── Jobs │ ├── RefreshCache.php │ └── UpdateLaraCacheModelsList.php ├── LaraCache.php ├── LaraCacheServiceProvider.php ├── Observers │ └── LaraCacheObserver.php ├── Traits │ ├── InteractsWithCache.php │ └── LaraCache.php └── Utils │ ├── CacheEnum.php │ └── Helpers.php └── tests ├── Feature ├── CacheDefaultValueTest.php ├── CacheEntityTest.php ├── CacheExpirationTest.php ├── CraeteCacheTest.php ├── DeleteCacheCommandTest.php ├── DeleteCacheTest.php ├── DeleteGroupCacheCommandTest.php ├── HelpersTest.php ├── QueueCacheTest.php ├── RestoreCacheTest.php ├── RetrieveCacheTest.php ├── UpdateCacheCommandTest.php ├── UpdateCacheTest.php ├── UpdateGroupCacheCommandTest.php └── UpdateLaraCacheModelsListTest.php ├── Pest.php ├── TestCase.php ├── TestSupport └── TestModels │ ├── QueueTestModel.php │ ├── TestModel.php │ ├── TestModel2.php │ └── TestUser.php └── Unit ├── CacheStatusTest.php ├── CommandDataTest.php ├── DeleteCacheActionTest.php ├── DeleteGroupCacheActionTest.php ├── UpdateCacheActionTest.php └── UpdateGroupCacheActionTest.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | tests: 9 | runs-on: ${{ matrix.os }} 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-latest] 15 | php: [8.0, 8.1, 8.2, 8.3, 8.4] 16 | laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] 17 | coverage: [none] 18 | dependency-version: [prefer-stable] 19 | include: 20 | - laravel: 10.* 21 | testbench: 8.* 22 | - laravel: 9.* 23 | testbench: 7.* 24 | - laravel: 8.* 25 | testbench: 6.* 26 | - laravel: 11.* 27 | testbench: 9.* 28 | - laravel: 12.* 29 | testbench: 10.* 30 | exclude: 31 | - laravel: 10.* 32 | php: 8.0 33 | - laravel: 11.* 34 | php: 8.0 35 | - laravel: 11.* 36 | php: 8.1 37 | - laravel: 12.* 38 | php: 8.0 39 | - laravel: 12.* 40 | php: 8.1 41 | 42 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} 43 | 44 | steps: 45 | - name: Update apt 46 | run: sudo apt-get update --fix-missing 47 | 48 | - name: Checkout code 49 | uses: actions/checkout@v4 50 | 51 | - name: Cache dependencies 52 | uses: actions/cache@v4 53 | with: 54 | path: ~/.composer/cache/files 55 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 56 | 57 | - name: Setup PHP 58 | uses: shivammathur/setup-php@v2 59 | with: 60 | php-version: ${{ matrix.php }} 61 | extensions: dom, curl, libxml, mbstring, zip, pcntl, sqlite3, pdo_sqlite, bcmath, fileinfo, xdebug 62 | tools: composer:v2 63 | ini-values: xdebug.mode="coverage" 64 | coverage: xdebug 65 | 66 | - name: Install dependencies 67 | run: | 68 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 69 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest 70 | 71 | - name: Execute tests 72 | run: composer test:ci 73 | 74 | - name: Upload coverage 75 | uses: codecov/codecov-action@v1 76 | with: 77 | token: ${{ secrets.CODECOV_TOKEN }} 78 | file: ./coverage.xml 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor 3 | .idea 4 | .phpunit.result.cache 5 | coverage.xml 6 | composer.lock 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Mostafa Zeinivand 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 | # LaraCache 2 | 3 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/mostafaznv/laracache/run-tests.yml?branch=master&label=Build&style=flat-square&logo=github)](https://github.com/mostafaznv/laracache/actions) 4 | [![Codecov branch](https://img.shields.io/codecov/c/github/mostafaznv/laracache/master.svg?style=flat-square&logo=codecov)](https://app.codecov.io/gh/mostafaznv/laracache) 5 | [![Quality Score](https://img.shields.io/scrutinizer/g/mostafaznv/laracache.svg?style=flat-square)](https://scrutinizer-ci.com/g/mostafaznv/laracache) 6 | [![GitHub license](https://img.shields.io/github/license/mostafaznv/laracache?style=flat-square)](https://github.com/mostafaznv/laracache/blob/master/LICENSE) 7 | [![Packagist Downloads](https://img.shields.io/packagist/dt/mostafaznv/laracache?style=flat-square&logo=packagist)](https://packagist.org/packages/mostafaznv/laracache) 8 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/mostafaznv/laracache.svg?style=flat-square&logo=composer)](https://packagist.org/packages/mostafaznv/laracache) 9 | 10 | ![laracache](https://github.com/mostafaznv/laracache/assets/7619687/13e909ba-2899-4922-a98b-f727ea26d564) 11 | 12 | Using this package, you can cache your heavy and most used queries. 13 | 14 | All you have to do is to define the `CacheEntity` objects in the model and specify a valid name and ttl for them. 15 | 16 | LaraCache will handle the rest of process automatically. It will create and update cache entities based on ttl that you've defined for each entity. 17 | 18 | Manually updating the cache entities of models after dispatching model events (creating, updating and deleting) isn't required, LaraCache manages them in the background and ensures the most up-to-date version of each cache entity. 19 | 20 | In addition to the core LaraCache package, I have developed a complementary package called [Nova LaraCache](https://github.com/mostafaznv/nova-laracache). Nova LaraCache seamlessly integrates LaraCache with [Laravel Nova](https://nova.laravel.com), the administration panel for Laravel applications. It offers a user-friendly interface within the Laravel Nova administration panel, enabling users to conveniently moderate and manage cache entities. 21 | 22 | 23 | ---- 24 | I am on an open-source journey 🚀, and I wish I could solely focus on my development path without worrying about my financial situation. However, as life is not perfect, I have to consider other factors. 25 | 26 | Therefore, if you decide to use my packages, please kindly consider making a donation. Any amount, no matter how small, goes a long way and is greatly appreciated. 🍺 27 | 28 | [![Donate](https://mostafaznv.github.io/donate/donate.svg)](https://mostafaznv.github.io/donate) 29 | 30 | ---- 31 | 32 | 33 | ## Requirements: 34 | 35 | - PHP 8.0.2 or higher 36 | - Laravel 8.40.0 or higher 37 | 38 | 39 | ## Installation 40 | 41 | 1. ##### Install the package via composer: 42 | ```shell 43 | composer require mostafaznv/laracache 44 | ``` 45 | 46 | 2. ##### Publish config file: 47 | ```shell 48 | php artisan vendor:publish --provider="Mostafaznv\LaraCache\LaraCacheServiceProvider" 49 | ``` 50 | 51 | 3. ##### Done 52 | 53 | 54 | ## Usage 55 | 56 | 1. ##### Add LaraCache trait to the model 57 | ```php 58 | cache(function() { 79 | return Article::query()->latest()->get(); 80 | }), 81 | 82 | CacheEntity::make('latest') 83 | ->validForRestOfDay() 84 | ->cache(function() { 85 | return Article::query()->latest()->first(); 86 | }) 87 | ]; 88 | } 89 | } 90 | ``` 91 | 92 | 2. ##### Retrieve Cache 93 | ```php 94 | use App\Models\Article; 95 | use Mostafaznv\LaraCache\Facades\LaraCache; 96 | 97 | 98 | $cache = Article::cache()->get('latest'); 99 | // or 100 | $cache = LaraCache::retrieve(Article::class, 'latest'); 101 | ``` 102 | 103 | 104 | ## Table of Contents: 105 | - [Installation](#installation) 106 | - [Install the package via composer](#install-the-package-via-composer) 107 | - [Publish config file](#publish-config-file) 108 | - [Usage](#usage) 109 | - [CacheEntity Methods](#cacheentity-methods) 110 | - [Disable/Enable Cache](#disableenable-cache) 111 | - [Enable](#enable) 112 | - [Disable](#disable) 113 | - [Update Cache Manually](#update-cache-manually) 114 | - [Update an Entity](#update-an-entity) 115 | - [Update all Entities](#update-all-entities) 116 | - [Update all LaraCache Entities](#update-all-laracache-entities) 117 | - [Delete Cache Manually](#delete-cache-manually) 118 | - [Delete an Entity](#delete-an-entity) 119 | - [Delete an Entity Forever](#delete-an-entity-forever) 120 | - [Delete all Model Entities](#delete-all-model-entities) 121 | - [Delete all Model Entities Forever](#delete-all-model-entities-forever) 122 | - [Delete all LaraCache Entities](#delete-all-laracache-entities) 123 | - [Artisan Commands](#artisan-commands) 124 | - [Update Cache](#update-cache) 125 | - [Delete Cache](#delete-cache) 126 | - [Group Operations](#group-operations) 127 | - [Laravel Nova Support](#laravel-nova-support) 128 | - [Config Properties](#config-properties) 129 | - [Complete Example](#complete-example) 130 | 131 | 132 | 133 | ## CacheEntity Methods 134 | 135 | | method | Arguments | description | 136 | |----------------------|-------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 137 | | setDriver | driver (type: `string`) | Specifies custom driver for cache entity | 138 | | isQueueable | status (type: `bool`, default: 'true')
onConnection (type: `string`, default: '')
onQueue (type: `string`, default: '') | This option specifies whether the cache operation should be performed in the background or not.
**Note**: By using the `onConnection` and `onQueue` arguments, you have the ability to specify custom connection and queue names for each cache entity. | 139 | | refreshAfterCreate | status (type: `bool`, default: `true`) | Specifies if the cache should refresh after create a record | 140 | | refreshAfterUpdate | status (type: `bool`, default: `true`) | Specifies if the cache should refresh after update a record | 141 | | refreshAfterDelete | status (type: `bool`, default: `true`) | Specifies if the cache should refresh after delete a record | 142 | | refreshAfterRestore | status (type: `bool`, default: `true`) | Specifies if the cache should refresh after restore a record | 143 | | forever | | Specifies that the cache should be valid forever | 144 | | validForRestOfDay | | Specify that cache entity should be valid till end of day | 145 | | validForRestOfWeek | | Specify that cache entity should be valid till end of week | 146 | | ttl | seconds (type: `int`) | Specifies cache time to live in second | 147 | | setDefault | defaultValue (type: `mixed`) | Specifies default value for the case that cache entity doesn't have any value | 148 | | cache | Closure | **Main** part of each cache entity. defines cache content | 149 | 150 | 151 | ## Disable/Enable Cache 152 | 153 | If you want to disable/enable cache, you can do it in the following two ways: 154 | 155 | ### Disable 156 | 157 | ```php 158 | use App\Models\Article; 159 | use Mostafaznv\LaraCache\Facades\LaraCache; 160 | 161 | 162 | Article::cache()->disable(); 163 | // or 164 | LaraCache::disable(Article::class); 165 | ``` 166 | 167 | 168 | ### Enable 169 | 170 | ```php 171 | use App\Models\Article; 172 | use Mostafaznv\LaraCache\Facades\LaraCache; 173 | 174 | 175 | Article::cache()->enable(); 176 | // or 177 | LaraCache::enable(Article::class); 178 | ``` 179 | 180 | ## Update Cache Manually 181 | 182 | Sometimes you want to update cache entities manually. 183 | 184 | ### Update an Entity 185 | 186 | ```php 187 | use App\Models\Article; 188 | use Mostafaznv\LaraCache\Facades\LaraCache; 189 | 190 | 191 | Article::cache()->update('latest'); 192 | // or 193 | LaraCache::update(Article::class, 'latest'); 194 | ``` 195 | 196 | ### Update all Entities 197 | 198 | ```php 199 | use App\Models\Article; 200 | use Mostafaznv\LaraCache\Facades\LaraCache; 201 | 202 | 203 | Article::cache()->updateAll(); 204 | // or 205 | LaraCache::updateAll(Article::class); 206 | ``` 207 | 208 | ### Update all LaraCache Entities 209 | 210 | This will update all cache entities that stored using LaraCache (across all models) 211 | 212 | ```php 213 | use App\Models\Article; 214 | use Mostafaznv\LaraCache\Facades\LaraCache; 215 | 216 | 217 | LaraCache::updateAll(); 218 | ``` 219 | 220 | ## Delete Cache Manually 221 | 222 | Sometimes you want to delete cache entities manually. using these methods, you can do it. 223 | 224 | ### Delete an Entity 225 | 226 | Using this feature, you can delete cache entities temporary. after spending ttl, cache entity will be generated again. 227 | 228 | ```php 229 | use App\Models\Article; 230 | use Mostafaznv\LaraCache\Facades\LaraCache; 231 | 232 | 233 | Article::cache()->delete('latest'); 234 | // or 235 | LaraCache::delete(Article::class, 'latest'); 236 | ``` 237 | 238 | ### Delete an Entity Forever 239 | 240 | Using this feature, you can delete cache entities permanently. Cache item will be deleted forever and whenever you try to retrieve it, you will get null (or default value). 241 | 242 | 243 | 244 | ```php 245 | use App\Models\Article; 246 | use Mostafaznv\LaraCache\Facades\LaraCache; 247 | 248 | 249 | Article::cache()->delete('latest', true); 250 | // or 251 | LaraCache::delete(Article::class, 'latest', true); 252 | ``` 253 | 254 | > Note: Cache Entity will update after creating or updating records in your model 255 | 256 | 257 | ### Delete all Model Entities 258 | 259 | ```php 260 | use App\Models\Article; 261 | use Mostafaznv\LaraCache\Facades\LaraCache; 262 | 263 | 264 | Article::cache()->deleteAll(); 265 | // or 266 | LaraCache::deleteAll(Article::class); 267 | ``` 268 | 269 | 270 | 271 | ### Delete all Model Entities Forever 272 | 273 | ```php 274 | use App\Models\Article; 275 | use Mostafaznv\LaraCache\Facades\LaraCache; 276 | 277 | 278 | Article::cache()->deleteAll(true); 279 | // or 280 | LaraCache::deleteAll(Article::class, true); 281 | ``` 282 | 283 | 284 | 285 | ### Delete all LaraCache Entities 286 | 287 | This will delete all cache entities that stored using LaraCache (across all models) 288 | 289 | ```php 290 | use App\Models\Article; 291 | use Mostafaznv\LaraCache\Facades\LaraCache; 292 | 293 | LaraCache::deleteAll(); 294 | // forever 295 | LaraCache::deleteAll(forever: true); 296 | ``` 297 | 298 | 299 | 300 | 301 | ## Artisan Commands 302 | This feature allows you to update or delete multiple cache entities of one or more models from the console command. This means you can programmatically control the cache data outside the caching cycle. 303 | 304 | You can also create groups of models and their entities in the config file and easily update or delete all their entities at once. 305 | 306 | ### Update Cache 307 | ```shell 308 | # updates all entities of article model 309 | php artisan laracache:update -m Article 310 | 311 | # updates specified entities of article model 312 | php artisan laracache:update -m Article -e latest -e featured 313 | 314 | # updates all entities of article and product models 315 | php artisan laracache:update -m Article -m Product 316 | 317 | # defines model with full namespace 318 | php artisan laracache:update -m "Domain\Article\Models\Article" 319 | ``` 320 | 321 | ### Delete Cache 322 | ```shell 323 | # deletes all entities of article model 324 | php artisan laracache:delete -m Article 325 | 326 | # deletes specified entities of article model 327 | php artisan laracache:delete -m Article -e latest -e featured 328 | 329 | # deletes all entities of article and product models 330 | php artisan laracache:delete -m Article -m Product 331 | 332 | # defines model with full namespace 333 | php artisan laracache:delete -m "Domain\Article\Models\Article" 334 | ``` 335 | 336 | > **Note**: If you don't specify any entity, all entities will be operated. 337 | 338 | > **Note**: If you specify multiple models, you can't specify any entity and all entities of all models will be operated. 339 | 340 | 341 | ### Group Operations 342 | ```shell 343 | # updates all entities of models that are in group-1 344 | php artisan laracache:update-group group-1 345 | 346 | # deletes all entities of models that are in group-1 347 | php artisan laracache:delete-group group-1 348 | ``` 349 | 350 | This is an example of a group configuration: 351 | ```php 352 | # config/laracache.php 353 | return [ 354 | // ... 355 | 'groups' => [ 356 | 'group-1' => [ 357 | [ 358 | 'model' => \App\Models\User::class, 359 | 'entities' => [ 360 | 'users.latest', 'users.featured' 361 | ], 362 | ], 363 | [ 364 | 'model' => \App\Models\Article::class, 365 | 'entities' => [], 366 | ] 367 | ], 368 | 369 | 'group-2' => [ 370 | [ 371 | 'model' => \App\Models\Article::class, 372 | 'entities' => [ 373 | 'featured-list', 'latest' 374 | ], 375 | ], 376 | [ 377 | 'model' => \App\Models\User::class, 378 | 'entities' => ['users.latest'], 379 | ] 380 | ], 381 | ] 382 | ]; 383 | ``` 384 | 385 | ## Laravel Nova Support 386 | [Nova LaraCache](https://github.com/mostafaznv/nova-laracache) is a powerful Laravel Nova package that extends the functionalities of LaraCache by integrating it seamlessly with Laravel Nova. It provides an intuitive interface within the Laravel Nova administration panel, allowing users to effortlessly moderate and manage cache entities. 387 | 388 | With Nova LaraCache, users can conveniently monitor cache expiration dates, review cache entity contents, regenerate cache items, and delete specific cache entries. 389 | 390 | To unlock the cache management capabilities provided by Nova LaraCache, please refer to the installation instructions and consult the LaraCache [documentation](https://github.com/mostafaznv/nova-laracache) for guidance on creating cache entities for each model. 391 | 392 | ## Config Properties 393 | 394 | | method | Type | description | 395 | |-------------------|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 396 | | driver | string (default: `null`) | The default mechanism for handling cache storage.
If you keep this option `null`, LaraCache will use the default cache storage from `config/cache.php` | 397 | | laracache-list | string (default: `laracache.list`) | LaraCache uses a separate list to store name of all entities. using these keys, we can perform some actions to all entities (such as update or delete them) | 398 | | first-day-of-week | integer (default: `0`) | In some regions, saturday is first day of the week and in another regions it may be different. you can change the first day of a week by changing this property | 399 | | last-day-of-week | integer (default: `6`) | In some regions, friday is last day of the week and in another regions it may be different. you can change the last day of a week by changing this property | 400 | | queue.status | bool (default: `false`) | Sometimes caching process is very heavy, so you have to queue the process and do it in background. | 401 | | queue.name | string (default: `default`) | You have the option to set custom name for the queue process. This name will be used when invoking the `onQueue` method while dispatching the queue job. | 402 | | queue.connection | string (default: `null`) | You have the option to set custom connection for the queue process. This connection will be used when invoking the `onConnection` method while dispatching the queue job. | 403 | | groups | array (default: `[]`) | You can group some entities and perform some operations on them | 404 | 405 | 406 | ## Complete Example 407 | ```php 408 | forever() 429 | ->setDriver('redis') 430 | ->cache(function() { 431 | return Article::query()->latest()->get(); 432 | }), 433 | 434 | CacheEntity::make('list.day') 435 | ->isQueueable() 436 | ->validForRestOfDay() 437 | ->cache(function() { 438 | return Article::query()->latest()->get(); 439 | }), 440 | 441 | CacheEntity::make('list.week') 442 | ->isQueueable(true, 'redis', 'low') 443 | ->validForRestOfWeek() 444 | ->cache(function() { 445 | return Article::query()->latest()->get(); 446 | }), 447 | 448 | CacheEntity::make('list.ttl') 449 | ->ttl(120) 450 | ->cache(function() { 451 | return Article::query()->latest()->get(); 452 | }), 453 | 454 | CacheEntity::make('latest') 455 | ->forever() 456 | ->cache(function() { 457 | return Article::query()->latest()->first(); 458 | }), 459 | 460 | CacheEntity::make('latest.no-create') 461 | ->refreshAfterCreate(false) 462 | ->cache(function() { 463 | return Article::query()->latest()->first(); 464 | }), 465 | 466 | CacheEntity::make('latest.no-update') 467 | ->refreshAfterUpdate(false) 468 | ->cache(function() { 469 | return Article::query()->latest()->first(); 470 | }), 471 | 472 | CacheEntity::make('latest.no-delete') 473 | ->refreshAfterDelete(false) 474 | ->cache(function() { 475 | return Article::query()->latest()->first(); 476 | }), 477 | 478 | CacheEntity::make('latest.no-restore') 479 | ->refreshAfterRestore(false) 480 | ->cache(function() { 481 | return Article::query()->latest()->first(); 482 | }), 483 | 484 | CacheEntity::make('empty.array') 485 | ->setDefault('empty value') 486 | ->cache(fn() => []), 487 | ]; 488 | } 489 | } 490 | ``` 491 | 492 | ---- 493 | I am on an open-source journey 🚀, and I wish I could solely focus on my development path without worrying about my financial situation. However, as life is not perfect, I have to consider other factors. 494 | 495 | Therefore, if you decide to use my packages, please kindly consider making a donation. Any amount, no matter how small, goes a long way and is greatly appreciated. 🍺 496 | 497 | [![Donate](https://mostafaznv.github.io/donate/donate.svg)](https://mostafaznv.github.io/donate) 498 | 499 | ---- 500 | 501 | 502 | 503 | ## License 504 | 505 | This software is released under [The MIT License (MIT)](LICENSE). 506 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mostafaznv/laracache", 3 | "description": "LaraCache is a customizable cache trait to cache queries on model's events", 4 | "keywords": [ 5 | "laravel", 6 | "eloquent", 7 | "orm", 8 | "cache", 9 | "redis", 10 | "memcache", 11 | "laravel 8", 12 | "laravel 9", 13 | "nova 4", 14 | "mostafaznv" 15 | ], 16 | "license": "MIT", 17 | "support": { 18 | "issues": "https://github.com/mostafaznv/laracache/issues", 19 | "source": "https://github.com/mostafaznv/laracache", 20 | "docs": "https://github.com/mostafaznv/laracache/blob/master/README.md" 21 | }, 22 | "authors": [ 23 | { 24 | "name": "mostafaznv", 25 | "email": "mostafa.zeinivand@gmail.com" 26 | } 27 | ], 28 | "minimum-stability": "dev", 29 | "prefer-stable": true, 30 | "require": { 31 | "php": "^8.0.2", 32 | "laravel/framework": "^8.40.0|^9.0|^10.0|^11.0|^12.0" 33 | }, 34 | "require-dev": { 35 | "phpunit/phpunit": "^9.5.10|^10.0|^11.5.3", 36 | "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", 37 | "pestphp/pest": "^1.20|^2.34|^3.7", 38 | "spatie/pest-plugin-test-time": "^1.0|^2.1" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Mostafaznv\\LaraCache\\": "src/" 43 | }, 44 | "files": [ 45 | "src/Utils/Helpers.php" 46 | ] 47 | }, 48 | "autoload-dev": { 49 | "psr-4": { 50 | "Mostafaznv\\LaraCache\\Tests\\": "tests" 51 | } 52 | }, 53 | "scripts": { 54 | "test": "vendor/bin/pest", 55 | "test:ci": "vendor/bin/pest --coverage --coverage-text --coverage-clover=coverage.xml" 56 | }, 57 | "config": { 58 | "sort-packages": true, 59 | "allow-plugins": { 60 | "pestphp/pest-plugin": true 61 | } 62 | }, 63 | "extra": { 64 | "laravel": { 65 | "providers": [ 66 | "Mostafaznv\\LaraCache\\LaraCacheServiceProvider" 67 | ] 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | null, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Cache List Key 21 | |-------------------------------------------------------------------------- 22 | | 23 | | LaraCache uses a separate list to store name of all entities. using these 24 | | keys, we can perform some actions to all entities (such as update or delete them) 25 | | 26 | */ 27 | 28 | 'laracache-list' => 'laracache.list', 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | First Day of Week 33 | |-------------------------------------------------------------------------- 34 | | 35 | | In some regions, saturday is first day of the week and in another regions 36 | | it may be different 37 | | 38 | */ 39 | 40 | 'first-day-of-week' => \Carbon\Carbon::SUNDAY, 41 | 42 | /* 43 | |-------------------------------------------------------------------------- 44 | | Last Day of Week 45 | |-------------------------------------------------------------------------- 46 | | 47 | | In some regions, friday is last day of the week and in another regions 48 | | it may be different 49 | | 50 | */ 51 | 52 | 'last-day-of-week' => \Carbon\Carbon::SATURDAY, 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Queue 57 | |-------------------------------------------------------------------------- 58 | | 59 | | Sometimes caching process is very heavy, so you have to queue the process and do it in background. 60 | | 61 | */ 62 | 63 | 'queue' => [ 64 | 'status' => false, 65 | 'name' => 'default', 66 | 'connection' => null 67 | ], 68 | 69 | /* 70 | |-------------------------------------------------------------------------- 71 | | Groups 72 | |-------------------------------------------------------------------------- 73 | | 74 | | You can group some entities and perform some actions on them. 75 | | 76 | | Example: 77 | | 'groups' => [ 78 | | 'group-1' => [ 79 | | [ 80 | | 'model' => \App\Models\User::class, 81 | | 'entities' => [ 82 | | 'featured', 'latest', 'popular' 83 | | ], 84 | | ], 85 | | [ 86 | | 'model' => \App\Models\Article::class, 87 | | 'entities' => [], 88 | | ] 89 | | ], 90 | | 91 | | 'group-2' => [ 92 | | [ 93 | | 'model' => \App\Models\Product::class, 94 | | 'entities' => [ 95 | | 'latest', 'popular' 96 | | ], 97 | | ], 98 | | [ 99 | | 'model' => \App\Models\Article::class, 100 | | 'entities' => [], 101 | | ] 102 | | ], 103 | | ], 104 | | 105 | */ 106 | 107 | 'groups' => [], 108 | ]; 109 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/ 7 | 8 | 9 | 10 | 11 | tests 12 | 13 | 14 | 15 | 16 | 17 | ./src 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Actions/DeleteCacheAction.php: -------------------------------------------------------------------------------- 1 | models) > 1) { 13 | foreach ($data->models as $model) { 14 | $this->deleteAll($model); 15 | } 16 | } 17 | else { 18 | $model = $data->models[0]; 19 | 20 | empty($data->entities) 21 | ? $this->deleteAll($model) 22 | : $this->delete($model, $data->entities); 23 | } 24 | } 25 | 26 | /** 27 | * @param \Mostafaznv\LaraCache\Traits\LaraCache $model 28 | * @return void 29 | */ 30 | private function deleteAll(string $model): void 31 | { 32 | $entities = []; 33 | 34 | foreach ($model::cacheEntities() as $entity) { 35 | $entities[] = $entity->name; 36 | } 37 | 38 | $this->delete($model, $entities); 39 | } 40 | 41 | /** 42 | * @param \Mostafaznv\LaraCache\Traits\LaraCache $model 43 | * @param array $entities 44 | * @return void 45 | */ 46 | private function delete(string $model, array $entities): void 47 | { 48 | $this->console?->warn( 49 | sprintf('>> Deleting cache entities in [%s] model', class_basename($model)) 50 | ); 51 | 52 | foreach ($entities as $entity) { 53 | $this->console?->line('— ' . $this->title($entity)); 54 | 55 | $model::cache()->delete($entity); 56 | 57 | $this->console?->info('Deleted'); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Actions/DeleteGroupCacheAction.php: -------------------------------------------------------------------------------- 1 | group($group); 12 | 13 | foreach ($group as $item) { 14 | DeleteCacheAction::make($this->console)->run($item); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Actions/Support/UpdateDeleteCache.php: -------------------------------------------------------------------------------- 1 | makeCommandDataFromGroupItem($item, $groupName); 49 | } 50 | 51 | return $data; 52 | } 53 | else { 54 | throw CacheGroupValueIsNotValid::make($groupName); 55 | } 56 | } 57 | 58 | private function makeCommandDataFromGroupItem(mixed $item, string $groupName): CommandData 59 | { 60 | if (isset($item['model']) and is_string($item['model']) and isset($item['entities'])) { 61 | return CommandData::make( 62 | models: [$item['model']], 63 | entities: $item['entities'] 64 | ); 65 | } 66 | else { 67 | throw CacheGroupValueIsNotValid::make($groupName); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Actions/UpdateCacheAction.php: -------------------------------------------------------------------------------- 1 | models) > 1) { 13 | foreach ($data->models as $model) { 14 | $this->updateAll($model); 15 | } 16 | } 17 | else { 18 | $model = $data->models[0]; 19 | 20 | empty($data->entities) 21 | ? $this->updateAll($model) 22 | : $this->update($model, $data->entities); 23 | } 24 | } 25 | 26 | /** 27 | * @param \Mostafaznv\LaraCache\Traits\LaraCache $model 28 | * @return void 29 | */ 30 | private function updateAll(string $model): void 31 | { 32 | $entities = []; 33 | 34 | foreach ($model::cacheEntities() as $entity) { 35 | $entities[] = $entity->name; 36 | } 37 | 38 | $this->update($model, $entities); 39 | } 40 | 41 | /** 42 | * @param \Mostafaznv\LaraCache\Traits\LaraCache $model 43 | * @param array $entities 44 | * @return void 45 | */ 46 | private function update(string $model, array $entities): void 47 | { 48 | $this->console?->warn( 49 | sprintf('>> Updating cache entities in [%s] model', class_basename($model)) 50 | ); 51 | 52 | foreach ($entities as $entity) { 53 | $this->console?->line('— ' . $this->title($entity)); 54 | 55 | $model::cache()->update($entity); 56 | 57 | $this->console?->info('Updated'); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Actions/UpdateGroupCacheAction.php: -------------------------------------------------------------------------------- 1 | group($group); 12 | 13 | foreach ($group as $item) { 14 | UpdateCacheAction::make($this->console)->run($item); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Cache.php: -------------------------------------------------------------------------------- 1 | retrieve($name); 17 | 18 | if ($withCacheData) { 19 | return $cache; 20 | } 21 | 22 | return $cache->value; 23 | } 24 | 25 | public function update(string $name): CacheData 26 | { 27 | $this->updateLaraCacheModelsList(); 28 | 29 | return $this->updateCacheEntity($name); 30 | } 31 | 32 | public function updateAll(): void 33 | { 34 | $this->updateLaraCacheModelsList(); 35 | 36 | foreach ($this->model::cacheEntities() as $entity) { 37 | $this->updateCacheEntity( 38 | name: $entity->name, 39 | entity: $entity 40 | ); 41 | } 42 | } 43 | 44 | public function delete(string $name, bool $forever = false): CacheData 45 | { 46 | return $this->deleteCacheEntity($name, $forever); 47 | } 48 | 49 | public function deleteAll(bool $forever = false): void 50 | { 51 | foreach ($this->model::cacheEntities() as $entity) { 52 | $this->deleteCacheEntity($entity->name, $forever, $entity); 53 | } 54 | } 55 | 56 | public function refresh(CacheEvent $event): void 57 | { 58 | if ($this->model::$isEnabled) { 59 | $this->updateLaraCacheModelsList(); 60 | 61 | foreach ($this->model::cacheEntities() as $entity) { 62 | if ($entity->isQueueable) { 63 | $this->initCache($entity, $entity->getTtl()); 64 | 65 | RefreshCache::dispatch($this->model, $entity->name, $event) 66 | ->onConnection($entity->queueConnection) 67 | ->onQueue($entity->queueName); 68 | } 69 | else { 70 | $this->updateCacheEntity($entity->name, $event, $entity); 71 | } 72 | } 73 | } 74 | } 75 | 76 | public function disable(): void 77 | { 78 | $this->model::$isEnabled = false; 79 | } 80 | 81 | public function enable(): void 82 | { 83 | $this->model::$isEnabled = true; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/CacheEntity.php: -------------------------------------------------------------------------------- 1 | name = $name; 119 | $this->driver = config('laracache.driver') ?? config('cache.default'); 120 | 121 | $queue = config('laracache.queue'); 122 | 123 | if (is_array($queue)) { 124 | $this->isQueueable = $queue['status'] ?? false; 125 | $this->queueName = $queue['name'] ?? 'default'; 126 | $this->queueConnection = $queue['connection'] ?? config('queue.default'); 127 | } 128 | else { 129 | $this->isQueueable = (bool)$queue; 130 | $this->queueName = 'default'; 131 | $this->queueConnection = config('queue.default'); 132 | } 133 | } 134 | 135 | /** 136 | * Create a new cache entity. 137 | * 138 | * @param string $name 139 | * @return CacheEntity 140 | */ 141 | public static function make(string $name): CacheEntity 142 | { 143 | return new static($name); 144 | } 145 | 146 | /** 147 | * Specify custom driver for cache entity 148 | * 149 | * @param string $driver 150 | * @return $this 151 | */ 152 | public function setDriver(string $driver): CacheEntity 153 | { 154 | $this->driver = $driver; 155 | 156 | return $this; 157 | } 158 | 159 | /** 160 | * Specify if cache operation should perform in background or not 161 | * 162 | * @param bool $status 163 | * @param string $onConnection 164 | * @param string $onQueue 165 | * @return $this 166 | */ 167 | public function isQueueable(bool $status = true, string $onConnection = '', string $onQueue = ''): CacheEntity 168 | { 169 | $this->isQueueable = $status; 170 | 171 | if ($onConnection) { 172 | $this->queueConnection = $onConnection; 173 | } 174 | 175 | if ($onQueue) { 176 | $this->queueName = $onQueue; 177 | } 178 | 179 | return $this; 180 | } 181 | 182 | /** 183 | * Specify that the cache should refresh after create a model instance 184 | * 185 | * @param bool $status 186 | * @return $this 187 | */ 188 | public function refreshAfterCreate(bool $status = true): CacheEntity 189 | { 190 | $this->refreshAfterCreate = $status; 191 | 192 | return $this; 193 | } 194 | 195 | /** 196 | * Specify that the cache should refresh after update a model instance 197 | * 198 | * @param bool $status 199 | * @return $this 200 | */ 201 | public function refreshAfterUpdate(bool $status = true): CacheEntity 202 | { 203 | $this->refreshAfterUpdate = $status; 204 | 205 | return $this; 206 | } 207 | 208 | /** 209 | * Specify that the cache should refresh after delete a model instance 210 | * 211 | * @param bool $status 212 | * @return $this 213 | */ 214 | public function refreshAfterDelete(bool $status = true): CacheEntity 215 | { 216 | $this->refreshAfterDelete = $status; 217 | 218 | return $this; 219 | } 220 | 221 | /** 222 | * Specify that the cache should refresh after restore a model instance 223 | * 224 | * @param bool $status 225 | * @return $this 226 | */ 227 | public function refreshAfterRestore(bool $status = true): CacheEntity 228 | { 229 | $this->refreshAfterRestore = $status; 230 | 231 | return $this; 232 | } 233 | 234 | /** 235 | * Specify that cache entity should exist there forever 236 | * 237 | * @return $this 238 | */ 239 | public function forever(): CacheEntity 240 | { 241 | $this->forever = true; 242 | $this->validForRestOfDay = false; 243 | $this->validForRestOfWeek = false; 244 | $this->ttl = 0; 245 | 246 | return $this; 247 | } 248 | 249 | /** 250 | * Specify that cache entity should exist there till end of day 251 | * 252 | * @return $this 253 | */ 254 | public function validForRestOfDay(): CacheEntity 255 | { 256 | $this->validForRestOfDay = true; 257 | $this->validForRestOfWeek = false; 258 | $this->forever = false; 259 | $this->ttl = 0; 260 | 261 | return $this; 262 | } 263 | 264 | /** 265 | * Specify that cache entity should exist there till end of week 266 | * 267 | * @return $this 268 | */ 269 | public function validForRestOfWeek(): CacheEntity 270 | { 271 | $this->validForRestOfDay = false; 272 | $this->validForRestOfWeek = true; 273 | $this->forever = false; 274 | $this->ttl = 0; 275 | 276 | return $this; 277 | } 278 | 279 | /** 280 | * Specify cache time to live in second 281 | * 282 | * @param int $seconds 283 | * @return $this 284 | */ 285 | public function ttl(int $seconds): CacheEntity 286 | { 287 | $this->ttl = max($seconds, 0); 288 | $this->forever = $this->ttl === 0; 289 | $this->validForRestOfDay = false; 290 | $this->validForRestOfWeek = false; 291 | 292 | return $this; 293 | } 294 | 295 | /** 296 | * Set default value of cache entity 297 | * 298 | * @param mixed $defaultValue 299 | * @return $this 300 | */ 301 | public function setDefault(mixed $defaultValue): CacheEntity 302 | { 303 | $this->default = $defaultValue; 304 | 305 | return $this; 306 | } 307 | 308 | /** 309 | * Get TTL 310 | * 311 | * @return int 312 | * @internal 313 | */ 314 | public function getTtl(): int 315 | { 316 | if ($this->forever) { 317 | return 0; 318 | } 319 | 320 | if ($this->validForRestOfDay) { 321 | return day_ending_seconds(); 322 | } 323 | 324 | if ($this->validForRestOfWeek) { 325 | return week_ending_seconds(); 326 | } 327 | 328 | return $this->ttl; 329 | } 330 | 331 | /** 332 | * Specify cache closure 333 | * 334 | * @param Closure $closure 335 | * @return $this 336 | */ 337 | public function cache(Closure $closure): CacheEntity 338 | { 339 | $this->cacheClosure = $closure; 340 | 341 | return $this; 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/Commands/DeleteCacheCommand.php: -------------------------------------------------------------------------------- 1 | option('model'); 18 | $entities = $this->option('entity'); 19 | 20 | DeleteCacheAction::make($this)->run( 21 | CommandData::make($models, $entities) 22 | ); 23 | 24 | 25 | return SymfonyCommand::SUCCESS; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Commands/DeleteGroupCacheCommand.php: -------------------------------------------------------------------------------- 1 | run( 17 | $this->argument('group') 18 | ); 19 | 20 | return SymfonyCommand::SUCCESS; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Commands/UpdateCacheCommand.php: -------------------------------------------------------------------------------- 1 | option('model'); 18 | $entities = $this->option('entity'); 19 | 20 | UpdateCacheAction::make($this)->run( 21 | CommandData::make($models, $entities) 22 | ); 23 | 24 | 25 | return SymfonyCommand::SUCCESS; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Commands/UpdateGroupCacheCommand.php: -------------------------------------------------------------------------------- 1 | run( 17 | $this->argument('group') 18 | ); 19 | 20 | return SymfonyCommand::SUCCESS; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DTOs/CacheData.php: -------------------------------------------------------------------------------- 1 | addSeconds($ttl)->unix() : null; 20 | 21 | return new static($status, $expiration, $value); 22 | } 23 | 24 | public static function fromCache(CacheEntity $entity, string $prefix, int $ttl = 0): self 25 | { 26 | $name = $prefix . '.' . $entity->name; 27 | $value = Cache::store($entity->driver)->get($name, $entity->default); 28 | 29 | if ($value === $entity->default) { 30 | return self::make(CacheStatus::NOT_CREATED(), $ttl, $entity->default); 31 | } 32 | 33 | return $value; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DTOs/CacheEvent.php: -------------------------------------------------------------------------------- 1 | 1 and count($entities)) { 22 | throw EntityIsNotAllowed::make(); 23 | } 24 | 25 | $m = []; 26 | 27 | foreach ($models as $model) { 28 | $m[] = self::model($model); 29 | } 30 | 31 | return new static($m, $entities); 32 | } 33 | 34 | 35 | private static function model(?string $model): string 36 | { 37 | if ($model) { 38 | if (self::modelExists($model)) { 39 | return $model; 40 | } 41 | 42 | // @codeCoverageIgnoreStart 43 | $defaultPath = "App\\Models\\$model"; 44 | 45 | if (self::modelExists($defaultPath)) { 46 | return $defaultPath; 47 | } 48 | // @codeCoverageIgnoreEnd 49 | } 50 | 51 | throw ModelDoestNotExist::make($model); 52 | } 53 | 54 | private static function modelExists(string $model): bool 55 | { 56 | if (class_exists($model)) { 57 | if (method_exists($model, 'cache')) { 58 | return true; 59 | } 60 | 61 | throw ModelDoesntUseLaraCacheTrait::make($model); 62 | } 63 | 64 | return false; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Exceptions/CacheEntityDoesNotExist.php: -------------------------------------------------------------------------------- 1 | model); 25 | 26 | $model->cache()->update($this->name, $this->event); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Jobs/UpdateLaraCacheModelsList.php: -------------------------------------------------------------------------------- 1 | driver = config('laracache.driver') ?: config('cache.default'); 24 | $this->key = self::LARACACHE_MODELS_LIST; 25 | 26 | $this->connection = config('laracache.queue.connection', config('queue.default')); 27 | $this->queue = config('laracache.queue.name', 'default'); 28 | } 29 | 30 | 31 | public function handle(): void 32 | { 33 | $list = Cache::driver($this->driver)->get($this->key, []); 34 | $list[] = $this->model; 35 | 36 | Cache::driver($this->driver)->forever( 37 | key: 'laracache.models', 38 | value: array_unique($list) 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LaraCache.php: -------------------------------------------------------------------------------- 1 | update($name, $event, $entity); 22 | } 23 | 24 | /** 25 | * Update All Cache Entities 26 | * 27 | * @param mixed $model 28 | */ 29 | public function updateAll(mixed $model = null): void 30 | { 31 | if ($model) { 32 | $model::cache()->updateAll(); 33 | } 34 | else { 35 | $list = self::list(); 36 | 37 | /** @var mixed $model */ 38 | foreach ($list as $model => $entities) { 39 | $model::cache()->updateAll(); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Delete Cache Entity 46 | * 47 | * @param mixed $model 48 | * @param string $name 49 | * @param bool $forever 50 | * @return mixed 51 | */ 52 | public function delete(mixed $model, string $name, bool $forever = false): mixed 53 | { 54 | return $model::cache()->delete($name, $forever); 55 | } 56 | 57 | /** 58 | * Delete All Cache Entities 59 | * 60 | * @param mixed $model 61 | * @param bool $forever 62 | */ 63 | public function deleteAll(mixed $model = null, bool $forever = false): void 64 | { 65 | if ($model) { 66 | $model::cache()->deleteAll($forever); 67 | } 68 | else { 69 | $list = self::list(); 70 | 71 | /** @var mixed $model */ 72 | foreach ($list as $model => $entities) { 73 | $model::cache()->deleteAll($forever); 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * Retrieve Cache 80 | * 81 | * @param mixed $model 82 | * @param string $name 83 | * @param bool $withCacheData 84 | * @return mixed 85 | */ 86 | public function retrieve(mixed $model, string $name, bool $withCacheData = false): mixed 87 | { 88 | return $model::cache()->get($name, $withCacheData); 89 | } 90 | 91 | /** 92 | * Disable refresh cache on all events 93 | * 94 | * @param $model 95 | */ 96 | public function disable($model): void 97 | { 98 | $model::cache()->disable(); 99 | } 100 | 101 | /** 102 | * Enable refresh cache on all events 103 | * 104 | * @param $model 105 | */ 106 | public function enable($model): void 107 | { 108 | $model::cache()->enable(); 109 | } 110 | 111 | /** 112 | * Retrieve List of All Cache Entities 113 | * 114 | * @return array 115 | */ 116 | public function list(): array 117 | { 118 | $laracacheListKey = config('laracache.laracache-list'); 119 | $driver = config('laracache.driver') ?? config('cache.default'); 120 | 121 | return Cache::store($driver)->get($laracacheListKey, []); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/LaraCacheServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 16 | $this->publishes([__DIR__ . '/../config/config.php' => config_path('laracache.php')], 'config'); 17 | } 18 | 19 | $this->commands([ 20 | UpdateCacheCommand::class, 21 | DeleteCacheCommand::class, 22 | UpdateGroupCacheCommand::class, 23 | DeleteGroupCacheCommand::class 24 | ]); 25 | } 26 | 27 | public function register(): void 28 | { 29 | $this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'laracache'); 30 | 31 | $this->app->bind('laracache', function () { 32 | return new LaraCache; 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Observers/LaraCacheObserver.php: -------------------------------------------------------------------------------- 1 | cache()->refresh(CacheEvent::CREATED()); 12 | } 13 | 14 | public function updated(mixed $model): void 15 | { 16 | if (!$this->isRestored($model)) { 17 | $model->cache()->refresh(CacheEvent::UPDATED()); 18 | } 19 | } 20 | 21 | public function deleted(mixed $model): void 22 | { 23 | $model->cache()->refresh(CacheEvent::DELETED()); 24 | } 25 | 26 | public function restored(mixed $model): void 27 | { 28 | $model->cache()->refresh(CacheEvent::RESTORED()); 29 | } 30 | 31 | private function isRestored(mixed $model): bool 32 | { 33 | return $model->wasChanged('deleted_at') 34 | and is_null($model->deleted_at) 35 | and !$model->originalIsEquivalent('deleted_at'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Traits/InteractsWithCache.php: -------------------------------------------------------------------------------- 1 | prefix = Str::kebab(class_basename($model)); 24 | $this->model = $model; 25 | $this->laracacheListKey = config('laracache.laracache-list'); 26 | } 27 | 28 | 29 | private function getEntityFullName(CacheEntity $entity): string 30 | { 31 | return $this->prefix . '.' . $entity->name; 32 | } 33 | 34 | private function findCacheEntity(string $name, ?CacheEntity $entity = null): CacheEntity 35 | { 36 | if ($entity) { 37 | return $entity; 38 | } 39 | 40 | foreach ($this->model::cacheEntities() as $cacheEntity) { 41 | if ($cacheEntity->name === $name) { 42 | return $cacheEntity; 43 | } 44 | } 45 | 46 | throw CacheEntityDoesNotExist::make($name, $this->model); 47 | } 48 | 49 | private function entityIsCallable(CacheEntity $entity, ?CacheEvent $event = null): bool 50 | { 51 | return is_null($event) 52 | or ($event->equals(CacheEvent::CREATED()) and $entity->refreshAfterCreate) 53 | or ($event->equals(CacheEvent::UPDATED()) and $entity->refreshAfterUpdate) 54 | or ($event->equals(CacheEvent::DELETED()) and $entity->refreshAfterDelete) 55 | or ($event->equals(CacheEvent::RESTORED()) and $entity->refreshAfterRestore); 56 | } 57 | 58 | private function callCacheClosure(CacheEntity $entity, int $ttl, bool $delete = false): CacheData 59 | { 60 | if ($delete) { 61 | return CacheData::make(CacheStatus::DELETED(), $ttl, $entity->default); 62 | } 63 | 64 | $value = $entity->cacheClosure ? call_user_func($entity->cacheClosure) : null; 65 | 66 | return CacheData::make( 67 | status: CacheStatus::CREATED(), 68 | ttl: $ttl, 69 | value: $value ?: $entity->default 70 | ); 71 | } 72 | 73 | private function updateCacheEntitiesList(CacheEntity $entity): void 74 | { 75 | $name = $this->getEntityFullName($entity); 76 | $list = Cache::store($entity->driver)->get($this->laracacheListKey); 77 | 78 | if (is_array($list)) { 79 | if (isset($list[$this->model]) and is_array($list[$this->model])) { 80 | if (!in_array($name, $list[$this->model])) { 81 | $list[$this->model][] = $name; 82 | } 83 | } 84 | else { 85 | $list[$this->model] = [$name]; 86 | } 87 | } 88 | else { 89 | $list = [ 90 | $this->model => [$name] 91 | ]; 92 | } 93 | 94 | Cache::store($entity->driver)->forever($this->laracacheListKey, $list); 95 | } 96 | 97 | private function putCacheIntoCacheStorage(CacheData $cache, string $driver, string $name, int $ttl): bool 98 | { 99 | if (is_null($cache->expiration)) { 100 | return Cache::store($driver)->forever($name, $cache); 101 | } 102 | 103 | return Cache::store($driver)->put($name, $cache, $ttl); 104 | } 105 | 106 | private function initCache(CacheEntity $entity, int $ttl): void 107 | { 108 | $name = $this->getEntityFullName($entity); 109 | $cache = CacheData::fromCache($entity, $this->prefix, $ttl); 110 | 111 | if ($cache->status->equals(CacheStatus::NOT_CREATED())) { 112 | $cache->value = $entity->default; 113 | } 114 | 115 | $cache->status = CacheStatus::CREATING(); 116 | 117 | $this->putCacheIntoCacheStorage($cache, $entity->driver, $name, $ttl); 118 | } 119 | 120 | private function storeCache(CacheData $cache, CacheEntity $entity, int $ttl): void 121 | { 122 | $name = $this->getEntityFullName($entity); 123 | 124 | $this->putCacheIntoCacheStorage($cache, $entity->driver, $name, $ttl); 125 | $this->updateCacheEntitiesList($entity); 126 | } 127 | 128 | private function updateLaraCacheModelsList(): void 129 | { 130 | UpdateLaraCacheModelsList::dispatch($this->model); 131 | } 132 | 133 | private function updateCacheEntity(string $name, ?CacheEvent $event = null, CacheEntity $entity = null): CacheData 134 | { 135 | $entity = $this->findCacheEntity($name, $entity); 136 | 137 | if ($this->entityIsCallable($entity, $event)) { 138 | $ttl = $entity->getTtl(); 139 | 140 | $this->initCache($entity, $ttl); 141 | $cache = $this->callCacheClosure($entity, $ttl); 142 | $this->storeCache($cache, $entity, $ttl); 143 | 144 | return $cache; 145 | } 146 | 147 | return CacheData::fromCache($entity, $this->prefix); 148 | } 149 | 150 | private function deleteCacheEntity(string $name, bool $deleteForever = false, CacheEntity $entity = null): CacheData 151 | { 152 | $entity = $this->findCacheEntity($name, $entity); 153 | $ttl = !$deleteForever ? $entity->getTtl() : 0; 154 | $cache = $this->callCacheClosure($entity, $ttl, true); 155 | $this->storeCache($cache, $entity, $ttl); 156 | 157 | return $cache; 158 | } 159 | 160 | private function retrieve(string $name): CacheData 161 | { 162 | $entity = $this->findCacheEntity($name); 163 | $cache = CacheData::fromCache($entity, $this->prefix); 164 | 165 | if ($cache->status->equals(CacheStatus::NOT_CREATED())) { 166 | if ($entity->isQueueable) { 167 | $this->initCache($entity, $entity->getTtl()); 168 | 169 | RefreshCache::dispatch($this->model, $entity->name, CacheEvent::RETRIEVED()) 170 | ->onConnection($entity->queueConnection) 171 | ->onQueue($entity->queueName); 172 | 173 | return CacheData::fromCache($entity, $this->prefix, $entity->ttl); 174 | } 175 | 176 | return $this->updateCacheEntity($name, null, $entity); 177 | } 178 | 179 | return $cache; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/Traits/LaraCache.php: -------------------------------------------------------------------------------- 1 | value; 17 | } 18 | 19 | public function equals(self $other): bool 20 | { 21 | return get_class($this) === get_class($other) and $this->getValue() === $other->getValue(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Utils/Helpers.php: -------------------------------------------------------------------------------- 1 | clone()->addWeeks($weeks); 16 | $end = $end->endOfWeek(config('laracache.last-day-of-week')); 17 | 18 | return $end->endOfDay()->diffInSeconds($now, true); 19 | } 20 | } 21 | 22 | if (!function_exists('day_ending_seconds')) { 23 | /** 24 | * Calculate time to end of the day in seconds 25 | * 26 | * @param int $days 27 | * @return int 28 | */ 29 | function day_ending_seconds(int $days = 0): int 30 | { 31 | $days = max($days, 0); 32 | 33 | $now = now(); 34 | $end = $now->clone()->addDays($days); 35 | 36 | return $end->endOfDay()->diffInSeconds($now, true); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Feature/CacheDefaultValueTest.php: -------------------------------------------------------------------------------- 1 | get('empty.number'); 7 | 8 | expect($cache)->toBe('empty value'); 9 | }); 10 | 11 | it('will return default value if cache content is an empty array', function() { 12 | $cache = TestModel::cache()->get('empty.array'); 13 | 14 | expect($cache)->toBe('empty value'); 15 | }); 16 | 17 | it('will return default value if cache content is an empty string', function() { 18 | $cache = TestModel::cache()->get('empty.string'); 19 | 20 | expect($cache)->toBe('empty value'); 21 | }); 22 | 23 | it('will return default value if cache content is false', function() { 24 | $cache = TestModel::cache()->get('empty.bool'); 25 | 26 | expect($cache)->toBe('empty value'); 27 | }); 28 | 29 | it('will return default value if cache content is null', function() { 30 | $cache = TestModel::cache()->get('empty.null'); 31 | 32 | expect($cache)->toBe('empty value'); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/Feature/CacheEntityTest.php: -------------------------------------------------------------------------------- 1 | entity = CacheEntity::make('test-name'); 7 | }); 8 | 9 | 10 | it('will make cache entity using factory', function() { 11 | expect($this->entity)->toBeInstanceOf(CacheEntity::class) 12 | ->name->toBe('test-name'); 13 | }); 14 | 15 | it('will set driver if to default cache driver if laracache.driver is null', function() { 16 | config()->set('cache.default', 'fake-driver'); 17 | 18 | $entity = CacheEntity::make('test-name'); 19 | 20 | expect($entity->driver)->toBe('fake-driver'); 21 | }); 22 | 23 | it('will set driver if laracache.driver is set', function() { 24 | config()->set('laracache.driver', 'fake-driver'); 25 | 26 | $entity = CacheEntity::make('test-name'); 27 | 28 | expect($entity->driver)->toBe('fake-driver'); 29 | }); 30 | 31 | it('will set is-queueable by default if laracache.queue is boolean', function() { 32 | expect($this->entity->isQueueable)->toBeFalse(); 33 | 34 | config()->set('laracache.queue', true); 35 | 36 | $entity = CacheEntity::make('test-name'); 37 | 38 | expect($entity->isQueueable)->toBeTrue(); 39 | }); 40 | 41 | it('will set is-queueable by default', function() { 42 | expect($this->entity->isQueueable)->toBeFalse(); 43 | 44 | config()->set('laracache.queue.status', true); 45 | 46 | $entity = CacheEntity::make('test-name'); 47 | 48 | expect($entity->isQueueable)->toBeTrue(); 49 | }); 50 | 51 | it('will set custom queue status', function() { 52 | expect($this->entity->isQueueable)->toBeFalse(); 53 | 54 | $this->entity->isQueueable(); 55 | expect($this->entity->isQueueable)->toBeTrue(); 56 | 57 | $this->entity->isQueueable(false); 58 | expect($this->entity->isQueueable)->toBeFalse(); 59 | }); 60 | 61 | it('will set queue name and connection by default', function() { 62 | expect($this->entity->queueName)->toBe('default') 63 | ->and($this->entity->queueConnection)->toBe('database'); 64 | }); 65 | 66 | it('will set queue name and connection by default with deprecated config file', function() { 67 | config()->set('laracache.queue', true); 68 | 69 | $entity = CacheEntity::make('test-name'); 70 | 71 | expect($entity->queueName)->toBe('default') 72 | ->and($entity->queueConnection)->toBe('database'); 73 | }); 74 | 75 | it('will set custom queue name and connection using config file', function() { 76 | expect($this->entity->queueName)->toBe('default') 77 | ->and($this->entity->queueConnection)->toBe('database'); 78 | 79 | config()->set('laracache.queue.name', 'test-queue'); 80 | config()->set('laracache.queue.connection', 'test-connection'); 81 | 82 | $entity = CacheEntity::make('test-name'); 83 | 84 | expect($entity->queueName)->toBe('test-queue') 85 | ->and($entity->queueConnection)->toBe('test-connection'); 86 | }); 87 | 88 | it('will set custom queue name and connection using entity method', function() { 89 | expect($this->entity->queueName)->toBe('default') 90 | ->and($this->entity->queueConnection)->toBe('database'); 91 | 92 | $entity = CacheEntity::make('test-name') 93 | ->isQueueable(true, 'test-connection', 'test-queue'); 94 | 95 | expect($entity->queueName)->toBe('test-queue') 96 | ->and($entity->queueConnection)->toBe('test-connection'); 97 | }); 98 | 99 | it('will set specific driver for entity', function() { 100 | expect($this->entity->driver)->toBe('array'); 101 | 102 | $this->entity->setDriver('fake-driver'); 103 | 104 | expect($this->entity->driver)->toBe('fake-driver'); 105 | }); 106 | 107 | it('will set refresh after create correctly', function() { 108 | expect($this->entity->refreshAfterCreate)->toBeTrue(); 109 | 110 | $this->entity->refreshAfterCreate(false); 111 | expect($this->entity->refreshAfterCreate)->toBeFalse(); 112 | 113 | $this->entity->refreshAfterCreate(true); 114 | expect($this->entity->refreshAfterCreate)->toBeTrue(); 115 | }); 116 | 117 | it('will set refresh after update correctly', function() { 118 | expect($this->entity->refreshAfterUpdate)->toBeTrue(); 119 | 120 | $this->entity->refreshAfterUpdate(false); 121 | expect($this->entity->refreshAfterUpdate)->toBeFalse(); 122 | 123 | $this->entity->refreshAfterUpdate(true); 124 | expect($this->entity->refreshAfterUpdate)->toBeTrue(); 125 | }); 126 | 127 | it('will set refresh after delete correctly', function() { 128 | expect($this->entity->refreshAfterDelete)->toBeTrue(); 129 | 130 | $this->entity->refreshAfterDelete(false); 131 | expect($this->entity->refreshAfterDelete)->toBeFalse(); 132 | 133 | $this->entity->refreshAfterDelete(true); 134 | expect($this->entity->refreshAfterDelete)->toBeTrue(); 135 | }); 136 | 137 | it('will set refresh after restore correctly', function() { 138 | expect($this->entity->refreshAfterRestore)->toBeTrue(); 139 | 140 | $this->entity->refreshAfterRestore(false); 141 | expect($this->entity->refreshAfterRestore)->toBeFalse(); 142 | 143 | $this->entity->refreshAfterRestore(true); 144 | expect($this->entity->refreshAfterRestore)->toBeTrue(); 145 | }); 146 | 147 | it('will set forever property correctly', function() { 148 | $this->entity->validForRestOfDay(); 149 | 150 | expect($this->entity) 151 | ->forever->toBeFalse() 152 | ->validForRestOfDay->toBeTrue() 153 | ->validForRestOfWeek->toBeFalse() 154 | ->ttl->toBe(0); 155 | 156 | $this->entity->forever(); 157 | 158 | expect($this->entity) 159 | ->forever->toBeTrue() 160 | ->validForRestOfDay->toBeFalse() 161 | ->validForRestOfWeek->toBeFalse() 162 | ->ttl->toBe(0); 163 | }); 164 | 165 | it('will set cache expire to till the end of day', function() { 166 | expect($this->entity) 167 | ->forever->toBeTrue() 168 | ->validForRestOfDay->toBeFalse(); 169 | 170 | $this->entity->validForRestOfDay(); 171 | 172 | expect($this->entity) 173 | ->forever->toBeFalse() 174 | ->validForRestOfDay->toBeTrue() 175 | ->validForRestOfWeek->toBeFalse() 176 | ->ttl->toBe(0); 177 | }); 178 | 179 | it('will set cache expire to till the week of day', function() { 180 | expect($this->entity) 181 | ->forever->toBeTrue() 182 | ->validForRestOfWeek->toBeFalse(); 183 | 184 | $this->entity->validForRestOfWeek(); 185 | 186 | expect($this->entity) 187 | ->forever->toBeFalse() 188 | ->validForRestOfDay->toBeFalse() 189 | ->validForRestOfWeek->toBeTrue() 190 | ->ttl->toBe(0); 191 | }); 192 | 193 | it('will set ttl correctly', function() { 194 | expect($this->entity->forever)->toBeTrue(); 195 | 196 | $this->entity->ttl(120); 197 | 198 | expect($this->entity) 199 | ->forever->toBeFalse() 200 | ->validForRestOfDay->toBeFalse() 201 | ->validForRestOfWeek->toBeFalse() 202 | ->ttl->toBe(120); 203 | }); 204 | 205 | it('will enable forever flag if ttl is zero', function() { 206 | $this->entity->validForRestOfDay(); 207 | 208 | expect($this->entity) 209 | ->forever->toBeFalse() 210 | ->validForRestOfDay->toBeTrue(); 211 | 212 | $this->entity->ttl(0); 213 | 214 | expect($this->entity) 215 | ->forever->toBeTrue() 216 | ->validForRestOfDay->toBeFalse() 217 | ->ttl->toBe(0); 218 | }); 219 | 220 | it('will set cache default value', function() { 221 | expect($this->entity->default)->toBeNull(); 222 | 223 | $this->entity->setDefault([]); 224 | expect($this->entity->default)->tobe([]); 225 | 226 | $this->entity->setDefault(0); 227 | expect($this->entity->default)->tobe(0); 228 | 229 | $object = new stdClass; 230 | $this->entity->setDefault($object); 231 | expect($this->entity->default)->tobe($object); 232 | }); 233 | 234 | it('will set cache function correctly', function() { 235 | expect($this->entity->cacheClosure)->toBeNull(); 236 | 237 | $this->entity->cache(function() { 238 | return 'test-value'; 239 | }); 240 | 241 | expect($this->entity->cacheClosure)->toBeInstanceOf(Closure::class); 242 | 243 | $this->entity->cache(fn() => 'return-value'); 244 | 245 | expect($this->entity->cacheClosure)->toBeInstanceOf(Closure::class); 246 | }); 247 | -------------------------------------------------------------------------------- /tests/Feature/CacheExpirationTest.php: -------------------------------------------------------------------------------- 1 | freeze('2022-05-17 12:43:34'); 8 | createModel(); 9 | }); 10 | 11 | it('will store cache entity forever', function() { 12 | $cache = TestModel::cache()->get('list.forever', true); 13 | 14 | expect($cache->value)->toHaveCount(1) 15 | ->and($cache->expiration)->toBeNull(); 16 | }); 17 | 18 | it('will store cache till end of day', function() { 19 | $cache = TestModel::cache()->get('list.day', true); 20 | 21 | expect($cache->value)->toHaveCount(1) 22 | ->and($cache->expiration)->toBe(1652831999); 23 | }); 24 | 25 | it('will store cache till end of week', function() { 26 | $cache = TestModel::cache()->get('list.week', true); 27 | 28 | expect($cache->value)->toHaveCount(1) 29 | ->and($cache->expiration)->toBe(1653177599); 30 | }); 31 | 32 | it('will store cache with ttl', function() { 33 | $cache = TestModel::cache()->get('list.ttl', true); 34 | 35 | expect($cache->value)->toHaveCount(1) 36 | ->and($cache->expiration)->toBe(1652791534); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/Feature/CraeteCacheTest.php: -------------------------------------------------------------------------------- 1 | get($name); 13 | 14 | expect($facadeCache)->toBeNull() 15 | ->and($cache)->toBeNull(); 16 | 17 | createModel(); 18 | 19 | $facadeCache = Cache::get($fullName); 20 | $cache = TestModel::cache()->get($name); 21 | 22 | expect($facadeCache->value->name)->toBe('test-name') 23 | ->and($cache)->name->toBe('test-name'); 24 | }); 25 | 26 | it('will not create cache after creating record if refresh-after-create flag is false', function() { 27 | $fullName = 'test-model.latest.no-create'; 28 | 29 | $hasCache = Cache::has($fullName); 30 | expect($hasCache)->toBeFalse(); 31 | 32 | $model = createModel(); 33 | 34 | $hasCache = Cache::has($fullName); 35 | expect($hasCache)->toBeFalse(); 36 | 37 | 38 | $model->name = 'new-test-name'; 39 | $model->save(); 40 | 41 | $hasCache = Cache::has($fullName); 42 | expect($hasCache)->toBeTrue(); 43 | }); 44 | 45 | it('will not create cache if cache is disabled', function() { 46 | TestModel::cache()->disable(); 47 | 48 | $hasCache = Cache::has('latest'); 49 | expect($hasCache)->toBeFalse(); 50 | 51 | createModel(); 52 | 53 | $hasCache = Cache::has('latest'); 54 | expect($hasCache)->toBeFalse(); 55 | 56 | TestModel::cache()->enable(); 57 | }); 58 | 59 | it('will not create cache if cache is disabled - facade', function() { 60 | LaraCache::disable(TestModel::class); 61 | 62 | $hasCache = Cache::has('latest'); 63 | expect($hasCache)->toBeFalse(); 64 | 65 | createModel(); 66 | 67 | $hasCache = Cache::has('latest'); 68 | expect($hasCache)->toBeFalse(); 69 | 70 | LaraCache::enable(TestModel::class); 71 | }); 72 | 73 | it('will store all cache entities in laracache.list', function() { 74 | $list = LaraCache::list(); 75 | expect($list)->toBeArray()->toHaveCount(0); 76 | 77 | createModel(); 78 | 79 | $list = LaraCache::list(); 80 | expect($list)->toHaveCount(1) 81 | ->and($list[TestModel::class])->toHaveCount(16) 82 | ->and($list[TestModel::class])->toContain( 83 | 'test-model.list.ttl', 'test-model.empty.number', 'test-model.latest.no-update' 84 | ); 85 | }); 86 | -------------------------------------------------------------------------------- /tests/Feature/DeleteCacheCommandTest.php: -------------------------------------------------------------------------------- 1 | 'test-model 1', 12 | 'content' => 'content 1' 13 | ], 14 | [ 15 | 'name' => 'test-model 2', 16 | 'content' => 'content 2' 17 | ] 18 | ]; 19 | 20 | foreach ($records as $record) { 21 | TestModel::query()->create($record); 22 | TestModel2::query()->create($record); 23 | } 24 | 25 | $this->created = CacheStatus::CREATED(); 26 | $this->deleted = CacheStatus::DELETED(); 27 | }); 28 | 29 | 30 | it('will delete all entities of all models if multiple models sent to the action', function() { 31 | $names = [ 32 | 'list.day', 'list.week', 'list.forever', 'latest' 33 | ]; 34 | 35 | foreach ($names as $name) { 36 | $cache = TestModel::cache()->get($name, true); 37 | expect($cache->status->equals($this->created))->toBeTrue(); 38 | 39 | $cache = TestModel2::cache()->get($name, true); 40 | expect($cache->status->equals($this->created))->toBeTrue(); 41 | } 42 | 43 | Artisan::call('laracache:delete', [ 44 | '--model' => [ 45 | TestModel::class, TestModel2::class 46 | ] 47 | ]); 48 | 49 | foreach ($names as $name) { 50 | $cache = TestModel::cache()->get($name, true); 51 | expect($cache->status->equals($this->deleted))->toBeTrue(); 52 | 53 | $cache = TestModel2::cache()->get($name, true); 54 | expect($cache->status->equals($this->deleted))->toBeTrue(); 55 | } 56 | }); 57 | 58 | it('will delete all entities of passed model if entities argument is empty', function() { 59 | $names = [ 60 | 'list.day', 'list.week', 'list.forever', 'latest' 61 | ]; 62 | 63 | foreach ($names as $name) { 64 | $cache = TestModel::cache()->get($name, true); 65 | expect($cache->status->equals($this->created))->toBeTrue(); 66 | } 67 | 68 | Artisan::call('laracache:delete', [ 69 | '-m' => [ 70 | TestModel::class 71 | ] 72 | ]); 73 | 74 | foreach ($names as $name) { 75 | $cache = TestModel::cache()->get($name, true); 76 | expect($cache->status->equals($this->deleted))->toBeTrue(); 77 | } 78 | }); 79 | 80 | it('will delete only specified entities if one model is sent to the action', function() { 81 | $names = [ 82 | 'list.week', 'list.forever', 'latest' 83 | ]; 84 | 85 | $cache = TestModel::cache()->get('list.day', true); 86 | expect($cache->status->equals($this->created))->toBeTrue(); 87 | 88 | Artisan::call('laracache:delete', [ 89 | '--model' => [ 90 | TestModel::class 91 | ], 92 | '--entity' => [ 93 | 'list.day' 94 | ] 95 | ]); 96 | 97 | foreach ($names as $name) { 98 | $cache = TestModel::cache()->get($name, true); 99 | expect($cache->status->equals($this->created))->toBeTrue(); 100 | } 101 | 102 | $cache = TestModel::cache()->get('list.day', true); 103 | expect($cache->status->equals($this->deleted))->toBeTrue(); 104 | }); 105 | 106 | it('will print the name of deleted cache entities in the console', function() { 107 | $entities = [ 108 | 'list.week', 'list.forever', 'latest' 109 | ]; 110 | 111 | $names = [ 112 | "List Week\nDeleted", "List Forever\nDeleted", "Latest" 113 | ]; 114 | 115 | 116 | Artisan::call('laracache:delete', [ 117 | '--model' => [ 118 | TestModel::class 119 | ], 120 | '--entity' => $entities 121 | ]); 122 | 123 | $output = Artisan::output(); 124 | 125 | expect($output) 126 | ->toBeString() 127 | ->toContain($names[0]) 128 | ->toContain($names[1]) 129 | ->toContain($names[2]); 130 | }); 131 | -------------------------------------------------------------------------------- /tests/Feature/DeleteCacheTest.php: -------------------------------------------------------------------------------- 1 | freeze('2022-05-17 12:43:34'); 13 | 14 | $this->model = createModel(); 15 | }); 16 | 17 | it('will throw exception if entity name is not defined during deleting cache', function() { 18 | $this->expectException(CacheEntityDoesNotExist::class); 19 | 20 | TestModel::cache()->delete('unknown-name'); 21 | }); 22 | 23 | it('will delete cache after deleting record', function() { 24 | $name = 'latest'; 25 | $fullName = 'test-model.latest'; 26 | 27 | $cache = TestModel::cache()->get($name); 28 | expect($cache->name)->toBe('test-name'); 29 | 30 | $this->model->delete(); 31 | 32 | $facadeCache = Cache::get($fullName); 33 | $cache = TestModel::cache()->get($name); 34 | 35 | expect($cache)->toBeNull() 36 | ->and($facadeCache->value)->toBeNull(); 37 | }); 38 | 39 | it('will not delete cache after deleting record if refresh-after-delete flag is false', function() { 40 | $name = 'latest.no-delete'; 41 | 42 | $cache = TestModel::cache()->get($name); 43 | expect($cache->name)->toBe('test-name'); 44 | 45 | $this->model->delete(); 46 | 47 | $cache = TestModel::cache()->get($name); 48 | expect($cache->name)->toBe('test-name'); 49 | }); 50 | 51 | it('will delete cache manually', function() { 52 | $latestCache = TestModel::cache()->get('latest'); 53 | $dayCache = TestModel::cache()->get('list.day', true); 54 | 55 | expect($latestCache->name)->toBe('test-name') 56 | ->and($dayCache->expiration)->toBe(1652831999); 57 | 58 | TestModel::cache()->delete('latest'); 59 | TestModel::cache()->delete('list.day'); 60 | 61 | $latestCache = TestModel::cache()->get('latest', true); 62 | $dayCache = TestModel::cache()->get('list.day', true); 63 | 64 | expect($latestCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 65 | ->and($latestCache->value)->toBeNull() 66 | ->and($latestCache->expiration)->toBeNull() 67 | ->and($dayCache->value)->toBeNull() 68 | ->and($dayCache->expiration)->toBe(1652831999); 69 | }); 70 | 71 | it('will delete cache manually using facade', function() { 72 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest'); 73 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day'); 74 | 75 | expect($latestCache->name)->toBe('test-name') 76 | ->and($dayCache)->toHaveCount(1); 77 | 78 | LaraCache::delete(TestModel::class, 'latest'); 79 | LaraCache::delete(TestModel::class, 'list.day'); 80 | 81 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest', true); 82 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 83 | 84 | expect($latestCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 85 | ->and($latestCache->value)->toBeNull() 86 | ->and($latestCache->expiration)->toBeNull() 87 | ->and($dayCache->status->equals(CacheStatus::DELETED())) 88 | ->and($dayCache->value)->toBeNull() 89 | ->and($dayCache->expiration)->toBe(1652831999); 90 | }); 91 | 92 | it('will delete cache manually forever', function() { 93 | $weekCache = TestModel::cache()->get('list.week', true); 94 | $dayCache = TestModel::cache()->get('list.day', true); 95 | 96 | expect($weekCache->expiration)->toBe(1653177599) 97 | ->and($dayCache->expiration)->toBe(1652831999); 98 | 99 | TestModel::cache()->delete('list.week'); 100 | TestModel::cache()->delete('list.day', true); 101 | 102 | $weekCache = TestModel::cache()->get('list.week', true); 103 | $dayCache = TestModel::cache()->get('list.day', true); 104 | 105 | expect($weekCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 106 | ->and($weekCache->expiration)->toBe(1653177599) 107 | ->and($dayCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 108 | ->and($dayCache->expiration)->toBeNull(); 109 | }); 110 | 111 | it('will delete cache manually forever using facade', function() { 112 | $weekCache = LaraCache::retrieve(TestModel::class, 'list.week', true); 113 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 114 | 115 | expect($weekCache->expiration)->toBe(1653177599) 116 | ->and($dayCache->expiration)->toBe(1652831999); 117 | 118 | LaraCache::delete(TestModel::class, 'list.week'); 119 | LaraCache::delete(TestModel::class, 'list.day', true); 120 | 121 | $weekCache = LaraCache::retrieve(TestModel::class, 'list.week', true); 122 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 123 | 124 | expect($weekCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 125 | ->and($weekCache->expiration)->toBe(1653177599) 126 | ->and($dayCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 127 | ->and($dayCache->expiration)->toBeNull(); 128 | }); 129 | 130 | it('will return default value after deleting cache item', function() { 131 | $cache = TestModel::cache()->get('static.number', true); 132 | 133 | expect($cache->status->equals(CacheStatus::CREATED()))->toBeTrue() 134 | ->and($cache->value)->toBe(1); 135 | 136 | TestModel::cache()->delete('static.number'); 137 | 138 | $cache = TestModel::cache()->get('static.number', true); 139 | 140 | expect($cache->status->equals(CacheStatus::DELETED()))->toBeTrue() 141 | ->and($cache->value)->toBe('default-value'); 142 | }); 143 | 144 | it('will not delete other entities during deleting an entity manually', function() { 145 | $latestCache = TestModel::cache()->get('latest'); 146 | $dayCache = TestModel::cache()->get('list.day'); 147 | 148 | expect($latestCache->name)->toBe('test-name') 149 | ->and($dayCache)->toHaveCount(1); 150 | 151 | TestModel::cache()->delete('latest'); 152 | 153 | $latestCache = TestModel::cache()->get('latest', true); 154 | $dayCache = TestModel::cache()->get('list.day', true); 155 | 156 | expect($latestCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 157 | ->and($latestCache->value)->toBeNull() 158 | ->and($dayCache->status->equals(CacheStatus::CREATED()))->toBeTrue() 159 | ->and($dayCache->value)->toHaveCount(1); 160 | }); 161 | 162 | it('will delete all cache entities of a model', function() { 163 | $weekCache = TestModel::cache()->get('list.week', true); 164 | $dayCache = TestModel::cache()->get('list.day', true); 165 | $latestCache = TestModel::cache()->get('latest', true); 166 | 167 | expect($weekCache->expiration)->toBe(1653177599) 168 | ->and($dayCache->expiration)->toBe(1652831999) 169 | ->and($latestCache->expiration)->toBeNull(); 170 | 171 | TestModel::cache()->deleteAll(); 172 | 173 | $weekCache = TestModel::cache()->get('list.week', true); 174 | $dayCache = TestModel::cache()->get('list.day', true); 175 | $latestCache = TestModel::cache()->get('latest', true); 176 | 177 | expect($weekCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 178 | ->and($weekCache->expiration)->toBe(1653177599) 179 | ->and($weekCache->value)->toBeNull() 180 | ->and($dayCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 181 | ->and($dayCache->expiration)->toBe(1652831999) 182 | ->and($dayCache->value)->toBeNull() 183 | ->and($latestCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 184 | ->and($latestCache->expiration)->toBeNull() 185 | ->and($dayCache->value)->toBeNull(); 186 | }); 187 | 188 | it('will delete all cache entities of a model using facade', function() { 189 | $weekCache = LaraCache::retrieve(TestModel::class, 'list.week', true); 190 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 191 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest', true); 192 | 193 | expect($weekCache->expiration)->toBe(1653177599) 194 | ->and($dayCache->expiration)->toBe(1652831999) 195 | ->and($latestCache->expiration)->toBeNull(); 196 | 197 | LaraCache::deleteAll(TestModel::class); 198 | 199 | $weekCache = LaraCache::retrieve(TestModel::class, 'list.week', true); 200 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 201 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest', true); 202 | 203 | expect($weekCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 204 | ->and($weekCache->expiration)->toBe(1653177599) 205 | ->and($weekCache->value)->toBeNull() 206 | ->and($dayCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 207 | ->and($dayCache->expiration)->toBe(1652831999) 208 | ->and($dayCache->value)->toBeNull() 209 | ->and($latestCache->status->equals(CacheStatus::DELETED()))->toBeTrue() 210 | ->and($latestCache->expiration)->toBeNull() 211 | ->and($dayCache->value)->toBeNull(); 212 | }); 213 | 214 | it('will delete all cache entities of a model forever', function() { 215 | $weekCache = TestModel::cache()->get('list.week', true); 216 | $dayCache = TestModel::cache()->get('list.day', true); 217 | $latestCache = TestModel::cache()->get('latest', true); 218 | 219 | expect($weekCache->expiration)->toBe(1653177599) 220 | ->and($dayCache->expiration)->toBe(1652831999) 221 | ->and($latestCache->expiration)->toBeNull(); 222 | 223 | TestModel::cache()->deleteAll(true); 224 | 225 | $weekCache = TestModel::cache()->get('list.week', true); 226 | $dayCache = TestModel::cache()->get('list.day', true); 227 | $latestCache = TestModel::cache()->get('latest', true); 228 | 229 | expect($weekCache->expiration)->toBeNull() 230 | ->and($dayCache->expiration)->toBeNull() 231 | ->and($latestCache->expiration)->toBeNull(); 232 | }); 233 | 234 | it('will delete all cache entities of a model forever using facade', function() { 235 | $weekCache = LaraCache::retrieve(TestModel::class, 'list.week', true); 236 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 237 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest', true); 238 | 239 | expect($weekCache->expiration)->toBe(1653177599) 240 | ->and($dayCache->expiration)->toBe(1652831999) 241 | ->and($latestCache->expiration)->toBeNull(); 242 | 243 | LaraCache::deleteAll(TestModel::class, true); 244 | 245 | $weekCache = LaraCache::retrieve(TestModel::class, 'list.week', true); 246 | $dayCache = LaraCache::retrieve(TestModel::class, 'list.day', true); 247 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest', true); 248 | 249 | expect($weekCache->expiration)->toBeNull() 250 | ->and($dayCache->expiration)->toBeNull() 251 | ->and($latestCache->expiration)->toBeNull(); 252 | }); 253 | 254 | it('will delete all cache entities that stored with laracache', function() { 255 | createModel2(); 256 | 257 | $weekCache1 = LaraCache::retrieve(TestModel::class, 'list.week', true); 258 | $dayCache1 = LaraCache::retrieve(TestModel::class, 'list.day', true); 259 | $latestCache1 = LaraCache::retrieve(TestModel::class, 'latest', true); 260 | $weekCache2 = LaraCache::retrieve(TestModel2::class, 'list.week', true); 261 | $dayCache2 = LaraCache::retrieve(TestModel2::class, 'list.day', true); 262 | $latestCache2 = LaraCache::retrieve(TestModel2::class, 'latest', true); 263 | 264 | expect($weekCache1->expiration)->toBe(1653177599) 265 | ->and($dayCache1->expiration)->toBe(1652831999) 266 | ->and($latestCache1->expiration)->toBeNull() 267 | ->and($weekCache2->expiration)->toBe(1653177599) 268 | ->and($dayCache2->expiration)->toBe(1652831999) 269 | ->and($latestCache2->expiration)->toBeNull(); 270 | 271 | LaraCache::deleteAll(); 272 | 273 | $weekCache1 = LaraCache::retrieve(TestModel::class, 'list.week', true); 274 | $dayCache1 = LaraCache::retrieve(TestModel::class, 'list.day', true); 275 | $latestCache1 = LaraCache::retrieve(TestModel::class, 'latest', true); 276 | $weekCache2 = LaraCache::retrieve(TestModel2::class, 'list.week', true); 277 | $dayCache2 = LaraCache::retrieve(TestModel2::class, 'list.day', true); 278 | $latestCache2 = LaraCache::retrieve(TestModel2::class, 'latest', true); 279 | 280 | expect($weekCache1->status->equals(CacheStatus::DELETED()))->toBeTrue() 281 | ->and($weekCache1->expiration)->toBe(1653177599) 282 | ->and($weekCache1->value)->toBeNull() 283 | ->and($dayCache1->status->equals(CacheStatus::DELETED()))->toBeTrue() 284 | ->and($dayCache1->expiration)->toBe(1652831999) 285 | ->and($dayCache1->value)->toBeNull() 286 | ->and($latestCache1->status->equals(CacheStatus::DELETED()))->toBeTrue() 287 | ->and($latestCache1->expiration)->toBeNull() 288 | ->and($dayCache1->value)->toBeNull() 289 | ->and($weekCache2->status->equals(CacheStatus::DELETED()))->toBeTrue() 290 | ->and($weekCache2->expiration)->toBe(1653177599) 291 | ->and($weekCache2->value)->toBeNull() 292 | ->and($dayCache2->status->equals(CacheStatus::DELETED()))->toBeTrue() 293 | ->and($dayCache2->expiration)->toBe(1652831999) 294 | ->and($dayCache2->value)->toBeNull() 295 | ->and($latestCache2->status->equals(CacheStatus::DELETED()))->toBeTrue() 296 | ->and($latestCache2->expiration)->toBeNull() 297 | ->and($dayCache2->value)->toBeNull(); 298 | }); 299 | 300 | it('will delete all cache entities that stored with laracache forever', function() { 301 | createModel2(); 302 | 303 | $weekCache1 = LaraCache::retrieve(TestModel::class, 'list.week', true); 304 | $dayCache1 = LaraCache::retrieve(TestModel::class, 'list.day', true); 305 | $latestCache1 = LaraCache::retrieve(TestModel::class, 'latest', true); 306 | $weekCache2 = LaraCache::retrieve(TestModel2::class, 'list.week', true); 307 | $dayCache2 = LaraCache::retrieve(TestModel2::class, 'list.day', true); 308 | $latestCache2 = LaraCache::retrieve(TestModel2::class, 'latest', true); 309 | 310 | expect($weekCache1->expiration)->toBe(1653177599) 311 | ->and($dayCache1->expiration)->toBe(1652831999) 312 | ->and($latestCache1->expiration)->toBeNull() 313 | ->and($weekCache2->expiration)->toBe(1653177599) 314 | ->and($dayCache2->expiration)->toBe(1652831999) 315 | ->and($latestCache2->expiration)->toBeNull(); 316 | 317 | LaraCache::deleteAll(forever: true); 318 | 319 | $weekCache1 = LaraCache::retrieve(TestModel::class, 'list.week', true); 320 | $dayCache1 = LaraCache::retrieve(TestModel::class, 'list.day', true); 321 | $latestCache1 = LaraCache::retrieve(TestModel::class, 'latest', true); 322 | $weekCache2 = LaraCache::retrieve(TestModel2::class, 'list.week', true); 323 | $dayCache2 = LaraCache::retrieve(TestModel2::class, 'list.day', true); 324 | $latestCache2 = LaraCache::retrieve(TestModel2::class, 'latest', true); 325 | 326 | expect($weekCache1->expiration)->toBeNull() 327 | ->and($dayCache1->expiration)->toBeNull() 328 | ->and($latestCache1->expiration)->toBeNull() 329 | ->and($weekCache2->expiration)->toBeNull() 330 | ->and($dayCache2->expiration)->toBeNull() 331 | ->and($latestCache2->expiration)->toBeNull(); 332 | }); 333 | -------------------------------------------------------------------------------- /tests/Feature/DeleteGroupCacheCommandTest.php: -------------------------------------------------------------------------------- 1 | 'test-model 1', 12 | 'content' => 'content 1' 13 | ], 14 | [ 15 | 'name' => 'test-model 2', 16 | 'content' => 'content 2' 17 | ] 18 | ]; 19 | 20 | foreach ($records as $record) { 21 | TestModel::query()->create($record); 22 | TestModel2::query()->create($record); 23 | } 24 | 25 | $this->created = CacheStatus::CREATED(); 26 | $this->deleted = CacheStatus::DELETED(); 27 | }); 28 | 29 | 30 | it('will delete all models and entities of the group', function() { 31 | config()->set('laracache.groups.test-group', [ 32 | [ 33 | 'model' => TestModel::class, 34 | 'entities' => [ 35 | 'list.forever', 'list.week' 36 | ], 37 | ], 38 | [ 39 | 'model' => TestModel2::class, 40 | 'entities' => [], 41 | ] 42 | ]); 43 | 44 | $names = [ 45 | "List Forever\nDeleted", "List Week\nDeleted", "List Day\nDeleted", "Latest\nDeleted" 46 | ]; 47 | 48 | 49 | Artisan::call('laracache:delete-group', [ 50 | 'group' => 'test-group' 51 | ]); 52 | 53 | $output = Artisan::output(); 54 | 55 | expect($output) 56 | ->toBeString() 57 | ->toContain($names[0]) 58 | ->toContain($names[1]) 59 | ->toContain($names[2]) 60 | ->toContain($names[3]); 61 | }); 62 | 63 | -------------------------------------------------------------------------------- /tests/Feature/HelpersTest.php: -------------------------------------------------------------------------------- 1 | freeze('2022-05-17 12:43:34'); 8 | }); 9 | 10 | 11 | it('will calculate time to end of the week in seconds', function() { 12 | $timeToEnd = week_ending_seconds(); 13 | expect($timeToEnd)->toBe(386185); 14 | 15 | $timeToEnd = week_ending_seconds(-20); 16 | expect($timeToEnd)->toBe(386185); 17 | 18 | $timeToEnd = week_ending_seconds(2); 19 | expect($timeToEnd)->toBe(1595785); 20 | }); 21 | 22 | it('will calculate time to end of the week when end of week is something custom', function() { 23 | config()->set('laracache.last-day-of-week', CarbonInterface::FRIDAY); 24 | 25 | $timeToEnd = week_ending_seconds(); 26 | expect($timeToEnd)->toBe(299785); 27 | }); 28 | 29 | it('will calculate time to end of the day in seconds', function() { 30 | $timeToEnd = day_ending_seconds(); 31 | expect($timeToEnd)->toBe(40585); 32 | 33 | $timeToEnd = day_ending_seconds(-20); 34 | expect($timeToEnd)->toBe(40585); 35 | 36 | $timeToEnd = day_ending_seconds(4); 37 | expect($timeToEnd)->toBe(386185); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/Feature/QueueCacheTest.php: -------------------------------------------------------------------------------- 1 | get('latest', true); 22 | $isCreating = $cache->status->equals(CacheStatus::CREATING()); 23 | 24 | expect($isCreating)->toBeTrue(); 25 | }); 26 | 27 | it('will initiate cache object with entity default value', function() { 28 | createQueueModel(); 29 | 30 | $cache = QueueTestModel::cache()->get('latest', true); 31 | 32 | expect($cache->value)->toBe(-1); 33 | }); 34 | 35 | it('will initiate cache object with properly expiration ttl', function() { 36 | createQueueModel(); 37 | 38 | $cache = QueueTestModel::cache()->get('latest', true); 39 | 40 | expect(is_null($cache->expiration))->toBeFalse(); 41 | }); 42 | 43 | it('will dispatch refresh-cache', function() { 44 | Queue::fake(); 45 | createQueueModel(); 46 | 47 | $onQueue = config('laracache.queue.name'); 48 | 49 | Queue::assertPushedOn($onQueue, RefreshCache::class); 50 | }); 51 | 52 | it('will create cache after processing queue', function() { 53 | createQueueModel(); 54 | $before = now(); 55 | 56 | $model = createQueueModel(); 57 | Artisan::call('queue:work --once'); 58 | 59 | $cache = QueueTestModel::cache()->get('latest', true); 60 | $isCreated = $cache->status->equals(CacheStatus::CREATED()); 61 | $after = now(); 62 | 63 | expect($before->diffInSeconds($after) >= 1)->toBeTrue() 64 | ->and($cache->value)->toBeInstanceOf(QueueTestModel::class) 65 | ->and($cache->value->name)->toBe($model->name) 66 | ->and($isCreated)->toBeTrue(); 67 | }); 68 | 69 | it('will return default value and dispatch cache creation job on retrieving entity', function() { 70 | Queue::fake(); 71 | DB::table('test_models') 72 | ->insert([ 73 | 'name' => 'queue-test-name', 74 | 'content' => 'content', 75 | 'created_at' => now() 76 | ]); 77 | 78 | $cache = QueueTestModel::cache()->get('latest', true); 79 | $isCreating = $cache->status->equals(CacheStatus::CREATING()); 80 | 81 | expect($isCreating)->toBeTrue() 82 | ->and($cache->value)->toBe(-1); 83 | 84 | $onQueue = config('laracache.queue.name'); 85 | 86 | Queue::assertPushedOn($onQueue, RefreshCache::class); 87 | }); 88 | 89 | it('will create cache in background on retrieving entity', function() { 90 | $name = 'queue-test-name'; 91 | $before = now(); 92 | 93 | DB::table('test_models') 94 | ->insert([ 95 | 'name' => $name, 96 | 'content' => 'content', 97 | 'created_at' => now() 98 | ]); 99 | 100 | $cache = QueueTestModel::cache()->get('latest', true); 101 | $isCreating = $cache->status->equals(CacheStatus::CREATING()); 102 | 103 | expect($isCreating)->toBeTrue() 104 | ->and($cache->value)->toBe(-1); 105 | 106 | Artisan::call('queue:work --once'); 107 | 108 | $cache = QueueTestModel::cache()->get('latest', true); 109 | $isCreated = $cache->status->equals(CacheStatus::CREATED()); 110 | $after = now(); 111 | 112 | expect($before->diffInSeconds($after) >= 1)->toBeTrue() 113 | ->and($cache->value)->toBeInstanceOf(QueueTestModel::class) 114 | ->and($cache->value->name)->toBe($name) 115 | ->and($isCreated)->toBeTrue(); 116 | }); 117 | 118 | it('will change cache status to creating on model update', function() { 119 | $model = createQueueModel(); 120 | 121 | $cache = QueueTestModel::cache()->get('latest', true); 122 | $isCreating = $cache->status->equals(CacheStatus::CREATING()); 123 | 124 | expect($isCreating)->toBeTrue(); 125 | 126 | Artisan::call('queue:work --once'); 127 | 128 | $cache = QueueTestModel::cache()->get('latest', true); 129 | $isCreated = $cache->status->equals(CacheStatus::CREATED()); 130 | 131 | expect($isCreated)->toBeTrue(); 132 | 133 | $model->name = 'new name'; 134 | $model->save(); 135 | 136 | $cache = QueueTestModel::cache()->get('latest', true); 137 | $isCreating = $cache->status->equals(CacheStatus::CREATING()); 138 | 139 | expect($isCreating)->toBeTrue(); 140 | 141 | Artisan::call('queue:work --once'); 142 | 143 | $cache = QueueTestModel::cache()->get('latest', true); 144 | $isCreated = $cache->status->equals(CacheStatus::CREATED()); 145 | 146 | expect($isCreated)->toBeTrue() 147 | ->and($cache->value->name)->toBe('new name'); 148 | }); 149 | 150 | it('will return old cache until queue process of updating model is done', function() { 151 | $model = createQueueModel('old-name'); 152 | 153 | $cache = QueueTestModel::cache()->get('latest'); 154 | expect($cache)->toBe(-1); 155 | 156 | Artisan::call('queue:work --once'); 157 | 158 | $cache = QueueTestModel::cache()->get('latest'); 159 | expect($cache->name)->toBe('old-name'); 160 | 161 | $model->name = 'new-name'; 162 | $model->save(); 163 | 164 | $cache = QueueTestModel::cache()->get('latest'); 165 | expect($cache->name)->toBe('old-name'); 166 | 167 | Artisan::call('queue:work --once'); 168 | 169 | $cache = QueueTestModel::cache()->get('latest'); 170 | expect($cache->name)->toBe('new-name'); 171 | }); 172 | -------------------------------------------------------------------------------- /tests/Feature/RestoreCacheTest.php: -------------------------------------------------------------------------------- 1 | get($name); 13 | expect($cache->name)->toBe('test-name'); 14 | 15 | $model->delete(); 16 | 17 | $facadeCache = Cache::get($fullName); 18 | $cache = TestModel::cache()->get($name); 19 | 20 | expect($facadeCache->value)->toBeNull() 21 | ->and($cache)->toBeNull(); 22 | 23 | $model->restore(); 24 | 25 | $facadeCache = Cache::get($fullName); 26 | $cache = TestModel::cache()->get($name); 27 | 28 | expect($facadeCache->value->name)->toBe('test-name') 29 | ->and($cache->name)->toBe('test-name'); 30 | }); 31 | 32 | it('will not restore cache after restoring record if refresh-after-restore flag is false', function() { 33 | $model = createModel(); 34 | $name = 'latest.no-restore'; 35 | $fullName = 'test-model.latest.no-restore'; 36 | 37 | $cache = Cache::get($fullName); 38 | expect($cache->value->name)->toBe('test-name'); 39 | 40 | $model->delete(); 41 | 42 | $cache = Cache::get($fullName); 43 | expect($cache->value)->toBeNull(); 44 | 45 | $model->restore(); 46 | 47 | $facadeCache = Cache::get($fullName); 48 | $cache = TestModel::cache()->get($name); 49 | 50 | expect($facadeCache->value)->toBeNull() 51 | ->and($cache)->toBeNull(); 52 | }); 53 | -------------------------------------------------------------------------------- /tests/Feature/RetrieveCacheTest.php: -------------------------------------------------------------------------------- 1 | expectException(CacheEntityDoesNotExist::class); 10 | 11 | TestModel::cache()->get('unknown-name'); 12 | }); 13 | 14 | it('will retrieve content of each entity correctly', function() { 15 | $numberCache = TestModel::cache()->get('static.number'); 16 | $arrayCache = TestModel::cache()->get('static.array'); 17 | $boolCache = TestModel::cache()->get('static.bool'); 18 | 19 | expect($numberCache)->toBe(1) 20 | ->and($arrayCache)->toBe([1, 2]) 21 | ->and($boolCache)->toBeTrue(); 22 | }); 23 | 24 | it('will retrieve content of each entity correctly using facade', function() { 25 | $cache = LaraCache::retrieve(TestModel::class, 'static.array'); 26 | 27 | expect($cache)->toBe([1, 2]); 28 | }); 29 | 30 | it('will retrieve cache using laraCache static method', function() { 31 | $numberCache = TestModel::laraCache()->get('static.number'); 32 | $arrayCache = TestModel::laraCache()->get('static.array'); 33 | $boolCache = TestModel::laraCache()->get('static.bool'); 34 | 35 | expect($numberCache)->toBe(1) 36 | ->and($arrayCache)->toBe([1, 2]) 37 | ->and($boolCache)->toBeTrue(); 38 | }); 39 | 40 | it('will return cache data if with cache data flag is true', function() { 41 | createModel(); 42 | 43 | $cache = TestModel::cache()->get('latest', true); 44 | 45 | expect($cache)->toBeInstanceOf(CacheData::class); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/Feature/UpdateCacheCommandTest.php: -------------------------------------------------------------------------------- 1 | getTable()) 11 | ->insert([ 12 | [ 13 | 'name' => 'test-model-1-1', 14 | 'content' => 'content 1-1' 15 | ], 16 | [ 17 | 'name' => 'test-model-1-2', 18 | 'content' => 'content 1-2' 19 | ] 20 | ]); 21 | 22 | DB::table((new TestModel2())->getTable()) 23 | ->insert([ 24 | [ 25 | 'name' => 'test-model-2-1', 26 | 'content' => 'content 2-1' 27 | ], 28 | [ 29 | 'name' => 'test-model-2-2', 30 | 'content' => 'content 2-2' 31 | ] 32 | ]); 33 | }); 34 | 35 | 36 | it('will update all entities of all models if multiple models sent to the action', function() { 37 | $names = [ 38 | 'list.day', 'list.week', 'list.forever', 'latest' 39 | ]; 40 | 41 | foreach ($names as $name) { 42 | $hasCache = Cache::has("test-model.$name"); 43 | expect($hasCache)->toBeFalse(); 44 | 45 | $hasCache = Cache::has("test-model-2.$name"); 46 | expect($hasCache)->toBeFalse(); 47 | } 48 | 49 | Artisan::call('laracache:update', [ 50 | '--model' => [ 51 | TestModel::class, TestModel2::class 52 | ] 53 | ]); 54 | 55 | foreach ($names as $name) { 56 | $hasCache = Cache::has("test-model.$name"); 57 | expect($hasCache)->toBeTrue(); 58 | 59 | $hasCache = Cache::has("test-model2.$name"); 60 | expect($hasCache)->toBeTrue(); 61 | } 62 | }); 63 | 64 | it('will update all entities of passed model if entities argument is empty', function() { 65 | $names = [ 66 | 'list.day', 'list.week', 'list.forever', 'latest' 67 | ]; 68 | 69 | foreach ($names as $name) { 70 | $hasCache = Cache::has("test-model.$name"); 71 | expect($hasCache)->toBeFalse(); 72 | } 73 | 74 | Artisan::call('laracache:update', [ 75 | '-m' => [ 76 | TestModel::class 77 | ] 78 | ]); 79 | 80 | foreach ($names as $name) { 81 | $hasCache = Cache::has("test-model.$name"); 82 | expect($hasCache)->toBeTrue(); 83 | } 84 | }); 85 | 86 | it('will update only specified entities if one model is sent to the action', function() { 87 | $names = [ 88 | 'list.week', 'list.forever', 'latest' 89 | ]; 90 | 91 | $hasCache = Cache::has('test-model.list.day'); 92 | expect($hasCache)->toBeFalse(); 93 | 94 | Artisan::call('laracache:update', [ 95 | '--model' => [ 96 | TestModel::class 97 | ], 98 | '--entity' => [ 99 | 'list.day' 100 | ] 101 | ]); 102 | 103 | foreach ($names as $name) { 104 | $hasCache = Cache::has("test-model.$name"); 105 | expect($hasCache)->toBeFalse(); 106 | } 107 | 108 | $hasCache = Cache::has('test-model.list.day'); 109 | expect($hasCache)->toBeTrue(); 110 | }); 111 | 112 | it('will print the name of updated cache entities in the console', function() { 113 | $entities = [ 114 | 'list.week', 'list.forever', 'latest' 115 | ]; 116 | 117 | $names = [ 118 | "List Week\nUpdated", "List Forever\nUpdated", "Latest\nUpdated" 119 | ]; 120 | 121 | 122 | Artisan::call('laracache:update', [ 123 | '--model' => [ 124 | TestModel::class 125 | ], 126 | '--entity' => $entities 127 | ]); 128 | 129 | $output = Artisan::output(); 130 | 131 | expect($output) 132 | ->toBeString() 133 | ->toContain($names[0]) 134 | ->toContain($names[1]) 135 | ->toContain($names[2]); 136 | }); 137 | 138 | -------------------------------------------------------------------------------- /tests/Feature/UpdateCacheTest.php: -------------------------------------------------------------------------------- 1 | model = createModel(); 12 | }); 13 | 14 | it('will throw exception if entity name is not defined during updating cache', function() { 15 | $this->expectException(CacheEntityDoesNotExist::class); 16 | 17 | TestModel::cache()->update('unknown-name'); 18 | }); 19 | 20 | it('will update cache after updating record', function() { 21 | $name = 'latest'; 22 | $fullName = 'test-model.latest'; 23 | 24 | $facadeCache = Cache::get($fullName); 25 | $cache = TestModel::cache()->get($name); 26 | 27 | expect($cache->name)->toBe('test-name') 28 | ->and($facadeCache->value->name)->toBe('test-name'); 29 | 30 | $this->model->name = 'new-test-name'; 31 | $this->model->save(); 32 | 33 | $facadeCache = Cache::get($fullName); 34 | $cache = TestModel::cache()->get($name); 35 | 36 | expect($cache->name)->toBe('new-test-name') 37 | ->and($facadeCache->value->name)->toBe('new-test-name'); 38 | }); 39 | 40 | it('will not update cache after updating record if refresh-after-update flag is false', function() { 41 | $name = 'latest.no-update'; 42 | $fullName = 'test-model.latest.no-update'; 43 | 44 | $cache = TestModel::cache()->get($name); 45 | expect($cache->name)->toBe('test-name'); 46 | 47 | $this->model->name = 'new-test-name'; 48 | $this->model->save(); 49 | 50 | $cacheFacade = Cache::get($fullName); 51 | $cache = TestModel::cache()->get($name); 52 | 53 | expect($cache->name)->toBe('test-name') 54 | ->and($cacheFacade->value->name)->toBe('test-name'); 55 | }); 56 | 57 | it('will update cache manually', function() { 58 | $name = 'latest'; 59 | 60 | $cache = TestModel::cache()->get($name); 61 | expect($cache->name)->toBe('test-name'); 62 | 63 | DB::table('test_models') 64 | ->where('id', $this->model->id) 65 | ->update([ 66 | 'name' => 'new-test-name' 67 | ]); 68 | 69 | $cache = TestModel::cache()->get($name); 70 | expect($cache->name)->toBe('test-name'); 71 | 72 | TestModel::cache()->update($name); 73 | 74 | $cache = TestModel::cache()->get($name); 75 | expect($cache->name)->toBe('new-test-name'); 76 | }); 77 | 78 | it('will update cache manually using facade', function() { 79 | $cache = LaraCache::retrieve(TestModel::class, 'latest'); 80 | expect($cache->name)->toBe('test-name'); 81 | 82 | DB::table('test_models') 83 | ->where('id', $cache->id) 84 | ->update([ 85 | 'name' => 'new-test-name' 86 | ]); 87 | 88 | $cache = LaraCache::retrieve(TestModel::class, 'latest'); 89 | expect($cache->name)->toBe('test-name'); 90 | 91 | LaraCache::update(TestModel::class, 'latest'); 92 | 93 | $cache = LaraCache::retrieve(TestModel::class, 'latest'); 94 | expect($cache->name)->toBe('new-test-name'); 95 | }); 96 | 97 | it('will update all cache entities', function() { 98 | $latestCache = TestModel::cache()->get('latest'); 99 | $foreverCache = TestModel::cache()->get('list.forever'); 100 | 101 | expect($latestCache->name)->toBe('test-name') 102 | ->and($foreverCache)->toHaveCount(1); 103 | 104 | DB::table('test_models')->insert([ 105 | 'name' => 'new-test-name', 106 | 'content' => 'content', 107 | 'created_at' => now()->addSecond() 108 | ]); 109 | 110 | $latestCache = TestModel::cache()->get('latest'); 111 | $foreverCache = TestModel::cache()->get('list.forever'); 112 | 113 | expect($latestCache->name)->toBe('test-name') 114 | ->and($foreverCache)->toHaveCount(1); 115 | 116 | TestModel::cache()->updateAll(); 117 | 118 | $latestCache = TestModel::cache()->get('latest'); 119 | $foreverCache = TestModel::cache()->get('list.forever'); 120 | 121 | expect($latestCache->name)->toBe('new-test-name') 122 | ->and($foreverCache)->toHaveCount(2); 123 | }); 124 | 125 | it('will update all cache entities manually using facade', function() { 126 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest'); 127 | $foreverCache = LaraCache::retrieve(TestModel::class, 'list.forever'); 128 | 129 | expect($latestCache->name)->toBe('test-name') 130 | ->and($foreverCache)->toHaveCount(1); 131 | 132 | DB::table('test_models')->insert([ 133 | 'name' => 'new-test-name', 134 | 'content' => 'content', 135 | 'created_at' => now()->addSecond() 136 | ]); 137 | 138 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest'); 139 | $foreverCache = LaraCache::retrieve(TestModel::class, 'list.forever'); 140 | 141 | expect($latestCache->name)->toBe('test-name') 142 | ->and($foreverCache)->toHaveCount(1); 143 | 144 | LaraCache::updateAll(TestModel::class); 145 | 146 | $latestCache = LaraCache::retrieve(TestModel::class, 'latest'); 147 | $foreverCache = LaraCache::retrieve(TestModel::class, 'list.forever'); 148 | 149 | expect($latestCache->name)->toBe('new-test-name') 150 | ->and($foreverCache)->toHaveCount(2); 151 | }); 152 | 153 | it('will update all cache entities that stored with laracache', function() { 154 | $model2 = createModel2(); 155 | 156 | $latestCache1 = TestModel::cache()->get('latest'); 157 | $dayCache1 = TestModel::cache()->get('list.day'); 158 | $latestCache2 = TestModel2::cache()->get('latest'); 159 | $dayCache2 = TestModel2::cache()->get('list.day'); 160 | 161 | expect($latestCache1->name)->toBe('test-name') 162 | ->and($dayCache1)->toHaveCount(1) 163 | ->and($latestCache2->name)->toBe('test-name-2') 164 | ->and($dayCache2)->toHaveCount(1); 165 | 166 | DB::table('test_models') 167 | ->where('id', $this->model->id) 168 | ->update([ 169 | 'name' => 'new-test-name' 170 | ]); 171 | 172 | DB::table('test_models_2') 173 | ->where('id', $model2->id) 174 | ->update([ 175 | 'name' => 'new-test-name-2' 176 | ]); 177 | 178 | $latestCache1 = TestModel::cache()->get('latest'); 179 | $dayCache1 = TestModel::cache()->get('list.day'); 180 | $latestCache2 = TestModel2::cache()->get('latest'); 181 | $dayCache2 = TestModel2::cache()->get('list.day'); 182 | 183 | expect($latestCache1->name)->toBe('test-name') 184 | ->and($dayCache1)->toHaveCount(1) 185 | ->and($latestCache2->name)->toBe('test-name-2') 186 | ->and($dayCache2)->toHaveCount(1); 187 | 188 | LaraCache::updateAll(); 189 | 190 | $latestCache1 = TestModel::cache()->get('latest'); 191 | $dayCache1 = TestModel::cache()->get('list.day'); 192 | $latestCache2 = TestModel2::cache()->get('latest'); 193 | $dayCache2 = TestModel2::cache()->get('list.day'); 194 | 195 | expect($latestCache1->name)->toBe('new-test-name') 196 | ->and($dayCache1)->toHaveCount(1) 197 | ->and($latestCache2->name)->toBe('new-test-name-2') 198 | ->and($dayCache2)->toHaveCount(1); 199 | }); 200 | -------------------------------------------------------------------------------- /tests/Feature/UpdateGroupCacheCommandTest.php: -------------------------------------------------------------------------------- 1 | getTable()) 10 | ->insert([ 11 | [ 12 | 'name' => 'test-model-1-1', 13 | 'content' => 'content 1-1' 14 | ], 15 | [ 16 | 'name' => 'test-model-1-2', 17 | 'content' => 'content 1-2' 18 | ] 19 | ]); 20 | 21 | DB::table((new TestModel2())->getTable()) 22 | ->insert([ 23 | [ 24 | 'name' => 'test-model-2-1', 25 | 'content' => 'content 2-1' 26 | ], 27 | [ 28 | 'name' => 'test-model-2-2', 29 | 'content' => 'content 2-2' 30 | ] 31 | ]); 32 | }); 33 | 34 | 35 | it('will update all models and entities of the group', function() { 36 | config()->set('laracache.groups.test-group', [ 37 | [ 38 | 'model' => TestModel::class, 39 | 'entities' => [ 40 | 'list.forever', 'list.week' 41 | ], 42 | ], 43 | [ 44 | 'model' => TestModel2::class, 45 | 'entities' => [], 46 | ] 47 | ]); 48 | 49 | $names = [ 50 | "List Forever\nUpdated", "List Week\nUpdated", "List Day\nUpdated", "Latest\nUpdated" 51 | ]; 52 | 53 | 54 | Artisan::call('laracache:update-group', [ 55 | 'group' => 'test-group' 56 | ]); 57 | 58 | $output = Artisan::output(); 59 | 60 | expect($output) 61 | ->toBeString() 62 | ->toContain($names[0]) 63 | ->toContain($names[1]) 64 | ->toContain($names[2]) 65 | ->toContain($names[3]); 66 | }); 67 | 68 | -------------------------------------------------------------------------------- /tests/Feature/UpdateLaraCacheModelsListTest.php: -------------------------------------------------------------------------------- 1 | set('queue.default', 'sync'); 9 | 10 | createModel(); 11 | createModel2(); 12 | }); 13 | 14 | 15 | it('will update the list of models which use laracache', function() { 16 | $models = Cache::get('laracache.models'); 17 | 18 | expect($models) 19 | ->toBeArray() 20 | ->toHaveCount(2) 21 | ->toMatchArray([ 22 | TestModel::class, 23 | TestModel2::class 24 | ]); 25 | }); 26 | 27 | it('wont duplicate models', function() { 28 | createModel(); 29 | $models = Cache::get('laracache.models'); 30 | 31 | expect($models)->toBeArray()->toHaveCount(2); 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | in(__DIR__); 9 | 10 | function createModel(?string $name = null): TestModel 11 | { 12 | $model = new TestModel; 13 | $model->name = $name ?? 'test-name'; 14 | $model->content = 'content'; 15 | $model->save(); 16 | 17 | return $model; 18 | } 19 | 20 | function createModel2(?string $name = null): TestModel2 21 | { 22 | $model = new TestModel2(); 23 | $model->name = $name ?? 'test-name-2'; 24 | $model->content = 'content'; 25 | $model->save(); 26 | 27 | return $model; 28 | } 29 | 30 | function createQueueModel(?string $name = null): QueueTestModel 31 | { 32 | $model = new QueueTestModel(); 33 | $model->name = $name ?? 'queue-test-name'; 34 | $model->content = 'content'; 35 | $model->save(); 36 | 37 | return $model; 38 | } 39 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | setUpDatabase($this->app); 17 | } 18 | 19 | /** 20 | * @param Application $app 21 | * 22 | * @return array 23 | */ 24 | protected function getPackageProviders($app): array 25 | { 26 | return [ 27 | LaraCacheServiceProvider::class, 28 | ]; 29 | } 30 | 31 | /** 32 | * @param Application $app 33 | */ 34 | public function getEnvironmentSetUp($app) 35 | { 36 | config()->set('cache.default', 'array'); 37 | config()->set('queue.default', 'database'); 38 | 39 | config()->set('database.default', 'sqlite'); 40 | config()->set('database.connections.sqlite', [ 41 | 'driver' => 'sqlite', 42 | 'database' => ':memory:', 43 | 'prefix' => '', 44 | ]); 45 | } 46 | 47 | /** 48 | * @param Application $app 49 | */ 50 | protected function setUpDatabase(Application $app) 51 | { 52 | $app['db']->connection() 53 | ->getSchemaBuilder() 54 | ->create('jobs', function(Blueprint $table) { 55 | $table->bigIncrements('id'); 56 | $table->string('queue')->index(); 57 | $table->longText('payload'); 58 | $table->unsignedTinyInteger('attempts'); 59 | $table->unsignedInteger('reserved_at')->nullable(); 60 | $table->unsignedInteger('available_at'); 61 | $table->unsignedInteger('created_at'); 62 | }); 63 | 64 | $app['db']->connection() 65 | ->getSchemaBuilder() 66 | ->create('failed_jobs', function(Blueprint $table) { 67 | $table->id(); 68 | $table->string('uuid')->unique(); 69 | $table->text('connection'); 70 | $table->text('queue'); 71 | $table->longText('payload'); 72 | $table->longText('exception'); 73 | $table->timestamp('failed_at')->useCurrent(); 74 | }); 75 | 76 | $app['db']->connection() 77 | ->getSchemaBuilder() 78 | ->create('test_models', function(Blueprint $table) { 79 | $table->increments('id'); 80 | $table->string('name'); 81 | $table->string('content', 500)->nullable(); 82 | $table->timestamps(); 83 | $table->softDeletes(); 84 | }); 85 | 86 | $app['db']->connection() 87 | ->getSchemaBuilder() 88 | ->create('test_models_2', function(Blueprint $table) { 89 | $table->increments('id'); 90 | $table->string('name'); 91 | $table->string('content', 500)->nullable(); 92 | $table->timestamps(); 93 | $table->softDeletes(); 94 | }); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/TestSupport/TestModels/QueueTestModel.php: -------------------------------------------------------------------------------- 1 | validForRestOfDay() 22 | ->isQueueable() 23 | ->setDefault(-1) 24 | ->cache(function() { 25 | sleep(1); 26 | 27 | return QueueTestModel::query()->latest()->first(); 28 | }) 29 | ]; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /tests/TestSupport/TestModels/TestModel.php: -------------------------------------------------------------------------------- 1 | forever() 21 | ->cache(function() { 22 | return TestModel::query()->latest()->get(); 23 | }), 24 | 25 | CacheEntity::make('list.day') 26 | ->validForRestOfDay() 27 | ->cache(function() { 28 | return TestModel::query()->latest()->get(); 29 | }), 30 | 31 | CacheEntity::make('list.week') 32 | ->validForRestOfWeek() 33 | ->cache(function() { 34 | return TestModel::query()->latest()->get(); 35 | }), 36 | 37 | CacheEntity::make('list.ttl') 38 | ->ttl(120) 39 | ->cache(function() { 40 | return TestModel::query()->latest()->get(); 41 | }), 42 | 43 | CacheEntity::make('latest') 44 | ->forever() 45 | ->cache(function() { 46 | return TestModel::query()->latest()->first(); 47 | }), 48 | 49 | CacheEntity::make('latest.no-create') 50 | ->refreshAfterCreate(false) 51 | ->cache(function() { 52 | return TestModel::query()->latest()->first(); 53 | }), 54 | 55 | CacheEntity::make('latest.no-update') 56 | ->refreshAfterUpdate(false) 57 | ->cache(function() { 58 | return TestModel::query()->latest()->first(); 59 | }), 60 | 61 | CacheEntity::make('latest.no-delete') 62 | ->refreshAfterDelete(false) 63 | ->cache(function() { 64 | return TestModel::query()->latest()->first(); 65 | }), 66 | 67 | CacheEntity::make('latest.no-restore') 68 | ->refreshAfterRestore(false) 69 | ->cache(function() { 70 | return TestModel::query()->latest()->first(); 71 | }), 72 | 73 | CacheEntity::make('empty.number') 74 | ->setDefault('empty value') 75 | ->cache(fn() => 0), 76 | 77 | CacheEntity::make('empty.array') 78 | ->setDefault('empty value') 79 | ->cache(fn() => []), 80 | 81 | CacheEntity::make('empty.string') 82 | ->setDefault('empty value') 83 | ->cache(fn() => ''), 84 | 85 | CacheEntity::make('empty.bool') 86 | ->setDefault('empty value') 87 | ->cache(fn() => false), 88 | 89 | CacheEntity::make('empty.null') 90 | ->setDefault('empty value') 91 | ->cache(fn() => null), 92 | 93 | CacheEntity::make('static.number') 94 | ->setDefault('default-value') 95 | ->cache(fn() => 1), 96 | 97 | CacheEntity::make('static.array') 98 | ->cache(fn() => [1, 2]), 99 | 100 | CacheEntity::make('static.bool') 101 | ->cache(fn() => true) 102 | ]; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /tests/TestSupport/TestModels/TestModel2.php: -------------------------------------------------------------------------------- 1 | forever() 22 | ->cache(function() { 23 | return TestModel2::query()->latest()->get(); 24 | }), 25 | 26 | CacheEntity::make('list.day') 27 | ->validForRestOfDay() 28 | ->cache(function() { 29 | return TestModel2::query()->latest()->get(); 30 | }), 31 | 32 | CacheEntity::make('list.week') 33 | ->validForRestOfWeek() 34 | ->cache(function() { 35 | return TestModel2::query()->latest()->get(); 36 | }), 37 | 38 | CacheEntity::make('list.ttl') 39 | ->ttl(120) 40 | ->cache(function() { 41 | return TestModel2::query()->latest()->get(); 42 | }), 43 | 44 | CacheEntity::make('latest') 45 | ->forever() 46 | ->cache(function() { 47 | return TestModel2::query()->latest()->first(); 48 | }) 49 | ]; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /tests/TestSupport/TestModels/TestUser.php: -------------------------------------------------------------------------------- 1 | equals(CacheStatus::CREATED()); 8 | expect($isEqual)->toBeTrue(); 9 | 10 | $isEqual = CacheStatus::CREATED()->equals(CacheStatus::DELETED()); 11 | expect($isEqual)->toBeFalse(); 12 | }); 13 | 14 | it('can get value of cache status', function(CacheStatus $status, string $value) { 15 | expect($status->getValue())->toBe($value); 16 | })->with([ 17 | 'NOT_CREATED' => ['status' => CacheStatus::NOT_CREATED(), 'value' => 'NOT_CREATED'], 18 | 'CREATING' => ['status' => CacheStatus::CREATING(), 'value' => 'CREATING'], 19 | 'CREATED' => ['status' => CacheStatus::CREATED(), 'value' => 'CREATED'], 20 | 'DELETED' => ['status' => CacheStatus::DELETED(), 'value' => 'DELETED'], 21 | ]); 22 | -------------------------------------------------------------------------------- /tests/Unit/CommandDataTest.php: -------------------------------------------------------------------------------- 1 | throws(EntityIsNotAllowed::class); 15 | 16 | it('will throw an exception if model does not exist', function() { 17 | CommandData::make(['Article']); 18 | })->throws(ModelDoestNotExist::class); 19 | 20 | it('will throw an exception if model does not use LaraCache trait', function() { 21 | CommandData::make([TestUser::class]); 22 | })->throws(ModelDoesntUseLaraCacheTrait::class); 23 | 24 | it('will assign existing models to models property', function() { 25 | $data = CommandData::make([TestModel::class, TestModel2::class]); 26 | 27 | expect($data->models) 28 | ->toBeArray() 29 | ->toHaveCount(2) 30 | ->toMatchArray([ 31 | TestModel::class, TestModel2::class 32 | ]); 33 | }); 34 | 35 | it('will assign entities to entities property', function() { 36 | $entities = ['latest', 'featured', 'popular']; 37 | $data = CommandData::make([TestModel::class], $entities); 38 | 39 | expect($data->entities) 40 | ->toBeArray() 41 | ->toHaveCount(3) 42 | ->toMatchArray($entities); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/Unit/DeleteCacheActionTest.php: -------------------------------------------------------------------------------- 1 | 'test-model 1', 13 | 'content' => 'content 1' 14 | ], 15 | [ 16 | 'name' => 'test-model 2', 17 | 'content' => 'content 2' 18 | ] 19 | ]; 20 | 21 | foreach ($records as $record) { 22 | TestModel::query()->create($record); 23 | TestModel2::query()->create($record); 24 | } 25 | 26 | $this->created = CacheStatus::CREATED(); 27 | $this->deleted = CacheStatus::DELETED(); 28 | }); 29 | 30 | 31 | it('will delete all entities of all models if multiple models sent to the action', function() { 32 | $names = [ 33 | 'list.day', 'list.week', 'list.forever', 'latest' 34 | ]; 35 | 36 | foreach ($names as $name) { 37 | $cache = TestModel::cache()->get($name, true); 38 | expect($cache->status->equals($this->created))->toBeTrue(); 39 | 40 | $cache = TestModel2::cache()->get($name, true); 41 | expect($cache->status->equals($this->created))->toBeTrue(); 42 | } 43 | 44 | DeleteCacheAction::make()->run( 45 | CommandData::make([TestModel::class, TestModel2::class]) 46 | ); 47 | 48 | foreach ($names as $name) { 49 | $cache = TestModel::cache()->get($name, true); 50 | expect($cache->status->equals($this->deleted))->toBeTrue(); 51 | 52 | $cache = TestModel2::cache()->get($name, true); 53 | expect($cache->status->equals($this->deleted))->toBeTrue(); 54 | } 55 | }); 56 | 57 | it('will delete all entities of passed model if entities argument is empty', function() { 58 | $names = [ 59 | 'list.day', 'list.week', 'list.forever', 'latest' 60 | ]; 61 | 62 | foreach ($names as $name) { 63 | $cache = TestModel::cache()->get($name, true); 64 | expect($cache->status->equals($this->created))->toBeTrue(); 65 | } 66 | 67 | DeleteCacheAction::make()->run( 68 | CommandData::make([TestModel::class]) 69 | ); 70 | 71 | foreach ($names as $name) { 72 | $cache = TestModel::cache()->get($name, true); 73 | expect($cache->status->equals($this->deleted))->toBeTrue(); 74 | } 75 | }); 76 | 77 | it('will delete only specified entities if one model is sent to the action', function() { 78 | $names = [ 79 | 'list.week', 'list.forever', 'latest' 80 | ]; 81 | 82 | $cache = TestModel::cache()->get('list.day', true); 83 | expect($cache->status->equals($this->created))->toBeTrue(); 84 | 85 | DeleteCacheAction::make()->run( 86 | CommandData::make([TestModel::class], ['list.day']) 87 | ); 88 | 89 | foreach ($names as $name) { 90 | $cache = TestModel::cache()->get($name, true); 91 | expect($cache->status->equals($this->created))->toBeTrue(); 92 | } 93 | 94 | $cache = TestModel::cache()->get('list.day', true); 95 | expect($cache->status->equals($this->deleted))->toBeTrue(); 96 | }); 97 | -------------------------------------------------------------------------------- /tests/Unit/DeleteGroupCacheActionTest.php: -------------------------------------------------------------------------------- 1 | 'test-model 1', 14 | 'content' => 'content 1' 15 | ], 16 | [ 17 | 'name' => 'test-model 2', 18 | 'content' => 'content 2' 19 | ] 20 | ]; 21 | 22 | foreach ($records as $record) { 23 | TestModel::query()->create($record); 24 | TestModel2::query()->create($record); 25 | } 26 | 27 | $this->created = CacheStatus::CREATED(); 28 | $this->deleted = CacheStatus::DELETED(); 29 | }); 30 | 31 | 32 | it('will throw an exception if group doesnt exist in config file', function() { 33 | DeleteGroupCacheAction::make()->run('test-group'); 34 | })->throws(CacheGroupNotExist::class); 35 | 36 | it('will throw an exception if group property is not an array', function() { 37 | config()->set('laracache.groups.test-group', 'test'); 38 | 39 | DeleteGroupCacheAction::make()->run('test-group'); 40 | })->throws(CacheGroupValueIsNotValid::class); 41 | 42 | it('will throw an exception if model key in group item is not set', function() { 43 | config()->set('laracache.groups.test-group', [ 44 | [ 45 | 'entities' => [ 46 | 'list.forever', 'list.week' 47 | ], 48 | ] 49 | ]); 50 | 51 | DeleteGroupCacheAction::make()->run('test-group'); 52 | })->throws(CacheGroupValueIsNotValid::class); 53 | 54 | it('will throw an exception if model key in group item is not a string', function() { 55 | config()->set('laracache.groups.test-group', [ 56 | [ 57 | 'model' => [TestModel::class], 58 | 'entities' => [ 59 | 'list.forever', 'list.week' 60 | ], 61 | ] 62 | ]); 63 | 64 | DeleteGroupCacheAction::make()->run('test-group'); 65 | })->throws(CacheGroupValueIsNotValid::class); 66 | 67 | it('will throw an exception if entities key in group item is not set', function() { 68 | config()->set('laracache.groups.test-group', [ 69 | [ 70 | 'model' => [TestModel::class], 71 | ] 72 | ]); 73 | 74 | DeleteGroupCacheAction::make()->run('test-group'); 75 | })->throws(CacheGroupValueIsNotValid::class); 76 | 77 | it('will throw an exception if entities key in group item is not an array', function() { 78 | config()->set('laracache.groups.test-group', [ 79 | [ 80 | 'model' => [TestModel::class], 81 | 'entities' => 'list.forever', 82 | ] 83 | ]); 84 | 85 | DeleteGroupCacheAction::make()->run('test-group'); 86 | })->throws(CacheGroupValueIsNotValid::class); 87 | 88 | it('will update all models and entities of the group', function() { 89 | config()->set('laracache.groups.test-group', [ 90 | [ 91 | 'model' => TestModel::class, 92 | 'entities' => [ 93 | 'list.forever', 'list.week' 94 | ], 95 | ], 96 | [ 97 | 'model' => TestModel2::class, 98 | 'entities' => [], 99 | ] 100 | ]); 101 | 102 | $entities1 = ['list.forever', 'list.week']; 103 | $entities2 = ['list.day', 'list.week', 'list.forever', 'latest']; 104 | 105 | foreach ($entities1 as $entity) { 106 | $cache = TestModel::cache()->get($entity, true); 107 | expect($cache->status->equals($this->created))->toBeTrue(); 108 | } 109 | 110 | foreach ($entities2 as $entity) { 111 | $cache = TestModel2::cache()->get($entity, true); 112 | expect($cache->status->equals($this->created))->toBeTrue(); 113 | } 114 | 115 | DeleteGroupCacheAction::make()->run('test-group'); 116 | 117 | 118 | foreach ($entities1 as $entity) { 119 | $cache = TestModel::cache()->get($entity, true); 120 | expect($cache->status->equals($this->deleted))->toBeTrue(); 121 | } 122 | 123 | foreach ($entities2 as $entity) { 124 | $cache = TestModel2::cache()->get($entity, true); 125 | expect($cache->status->equals($this->deleted))->toBeTrue(); 126 | } 127 | }); 128 | -------------------------------------------------------------------------------- /tests/Unit/UpdateCacheActionTest.php: -------------------------------------------------------------------------------- 1 | 'test-model-1', 14 | 'content' => 'content 1' 15 | ], 16 | [ 17 | 'name' => 'test-model-2', 18 | 'content' => 'content 2' 19 | ] 20 | ]; 21 | 22 | DB::table((new TestModel())->getTable())->insert($records); 23 | DB::table((new TestModel2())->getTable())->insert($records); 24 | }); 25 | 26 | 27 | it('will update all entities of all models if multiple models sent to the action', function() { 28 | $names = [ 29 | 'list.day', 'list.week', 'list.forever', 'latest' 30 | ]; 31 | 32 | foreach ($names as $name) { 33 | $hasCache = Cache::has("test-model.$name"); 34 | expect($hasCache)->toBeFalse(); 35 | 36 | $hasCache = Cache::has("test-model2.$name"); 37 | expect($hasCache)->toBeFalse(); 38 | } 39 | 40 | UpdateCacheAction::make()->run( 41 | CommandData::make([TestModel::class, TestModel2::class]) 42 | ); 43 | 44 | foreach ($names as $name) { 45 | $hasCache = Cache::has("test-model.$name"); 46 | expect($hasCache)->toBeTrue(); 47 | 48 | $hasCache = Cache::has("test-model2.$name"); 49 | expect($hasCache)->toBeTrue(); 50 | } 51 | }); 52 | 53 | it('will update all entities of passed model if entities argument is empty', function() { 54 | $names = [ 55 | 'list.day', 'list.week', 'list.forever', 'latest' 56 | ]; 57 | 58 | foreach ($names as $name) { 59 | $hasCache = Cache::has("test-model.$name"); 60 | expect($hasCache)->toBeFalse(); 61 | } 62 | 63 | UpdateCacheAction::make()->run( 64 | CommandData::make([TestModel::class]) 65 | ); 66 | 67 | foreach ($names as $name) { 68 | $hasCache = Cache::has("test-model.$name"); 69 | expect($hasCache)->toBeTrue(); 70 | } 71 | }); 72 | 73 | it('will update only specified entities if one model is sent to the action', function() { 74 | $names = [ 75 | 'list.week', 'list.forever', 'latest' 76 | ]; 77 | 78 | $hasCache = Cache::has('test-model.list.day'); 79 | expect($hasCache)->toBeFalse(); 80 | 81 | UpdateCacheAction::make()->run( 82 | CommandData::make([TestModel::class], ['list.day']) 83 | ); 84 | 85 | foreach ($names as $name) { 86 | $hasCache = Cache::has("test-model.$name"); 87 | expect($hasCache)->toBeFalse(); 88 | } 89 | 90 | $hasCache = Cache::has('test-model.list.day'); 91 | expect($hasCache)->toBeTrue(); 92 | }); 93 | -------------------------------------------------------------------------------- /tests/Unit/UpdateGroupCacheActionTest.php: -------------------------------------------------------------------------------- 1 | 'test-model-1', 15 | 'content' => 'content 1' 16 | ], 17 | [ 18 | 'name' => 'test-model-2', 19 | 'content' => 'content 2' 20 | ] 21 | ]; 22 | 23 | DB::table((new TestModel())->getTable())->insert($records); 24 | DB::table((new TestModel2())->getTable())->insert($records); 25 | }); 26 | 27 | 28 | it('will throw an exception if group doesnt exist in config file', function() { 29 | UpdateGroupCacheAction::make()->run('test-group'); 30 | })->throws(CacheGroupNotExist::class); 31 | 32 | it('will throw an exception if group property is not an array', function() { 33 | config()->set('laracache.groups.test-group', 'test'); 34 | 35 | UpdateGroupCacheAction::make()->run('test-group'); 36 | })->throws(CacheGroupValueIsNotValid::class); 37 | 38 | it('will throw an exception if model key in group item is not set', function() { 39 | config()->set('laracache.groups.test-group', [ 40 | [ 41 | 'entities' => [ 42 | 'list.forever', 'list.week' 43 | ], 44 | ] 45 | ]); 46 | 47 | UpdateGroupCacheAction::make()->run('test-group'); 48 | })->throws(CacheGroupValueIsNotValid::class); 49 | 50 | it('will throw an exception if model key in group item is not a string', function() { 51 | config()->set('laracache.groups.test-group', [ 52 | [ 53 | 'model' => [TestModel::class], 54 | 'entities' => [ 55 | 'list.forever', 'list.week' 56 | ], 57 | ] 58 | ]); 59 | 60 | UpdateGroupCacheAction::make()->run('test-group'); 61 | })->throws(CacheGroupValueIsNotValid::class); 62 | 63 | it('will throw an exception if entities key in group item is not set', function() { 64 | config()->set('laracache.groups.test-group', [ 65 | [ 66 | 'model' => [TestModel::class], 67 | ] 68 | ]); 69 | 70 | UpdateGroupCacheAction::make()->run('test-group'); 71 | })->throws(CacheGroupValueIsNotValid::class); 72 | 73 | it('will throw an exception if entities key in group item is not an array', function() { 74 | config()->set('laracache.groups.test-group', [ 75 | [ 76 | 'model' => [TestModel::class], 77 | 'entities' => 'list.forever', 78 | ] 79 | ]); 80 | 81 | UpdateGroupCacheAction::make()->run('test-group'); 82 | })->throws(CacheGroupValueIsNotValid::class); 83 | 84 | it('will update all models and entities of the group', function() { 85 | config()->set('laracache.groups.test-group', [ 86 | [ 87 | 'model' => TestModel::class, 88 | 'entities' => [ 89 | 'list.forever', 'list.week' 90 | ], 91 | ], 92 | [ 93 | 'model' => TestModel2::class, 94 | 'entities' => [], 95 | ] 96 | ]); 97 | 98 | $entities1 = ['list.forever', 'list.week']; 99 | $entities2 = ['list.day', 'list.week', 'list.forever', 'latest']; 100 | 101 | foreach ($entities1 as $entity) { 102 | $hasCache = Cache::has("test-model.$entity"); 103 | expect($hasCache)->toBeFalse(); 104 | } 105 | 106 | foreach ($entities2 as $entity) { 107 | $hasCache = Cache::has("test-model2.$entity"); 108 | expect($hasCache)->toBeFalse(); 109 | } 110 | 111 | UpdateGroupCacheAction::make()->run('test-group'); 112 | 113 | foreach ($entities1 as $entity) { 114 | $hasCache = Cache::has("test-model.$entity"); 115 | expect($hasCache)->toBeTrue(); 116 | } 117 | 118 | foreach ($entities2 as $entity) { 119 | $hasCache = Cache::has("test-model2.$entity"); 120 | expect($hasCache)->toBeTrue(); 121 | } 122 | }); 123 | --------------------------------------------------------------------------------