From 3df7db90754e1cbbe28433e024f8d4da5c9d1670 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 12 Mar 2023 21:00:42 -0300 Subject: [PATCH 01/10] Fixes constraints on GitHub actions --- .github/workflows/php.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 4fcb1d8..89cea85 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -53,22 +53,22 @@ jobs: - "8.0" - "8.1" - "8.2" - laravel-constrain: + laravel-constraintt: - "9.*" - "10.*" dependencies: - "lowest" - "highest" exclude: - - laravel-constrain: "10.*" + - laravel-constraint: "10.*" php-version: "8.0" steps: - name: "Set up PHP" uses: "shivammathur/setup-php@v2" with: php-version: "${{ matrix.php-version }}" - extensions: mbstring, intl - coverage: xdebug + extensions: "mbstring, intl" + coverage: "xdebug" - name: "Checkout code" uses: "actions/checkout@v3" @@ -77,6 +77,7 @@ jobs: uses: "ramsey/composer-install@v2" with: dependency-versions: "${{ matrix.dependencies }}" + composer-options: "--with=laravel/framework:${{ matrix.laravel-constraint }}" - name: "Execute unit tests" run: "composer run-script test" From 365d51a2bec252229118f9ca06f5cf0fcb2e3967 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 12 Mar 2023 21:05:11 -0300 Subject: [PATCH 02/10] Sets each badge into their own line. --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb878de..d1c9cbe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Cache Query -[![Latest Version on Packagist](https://img.shields.io/packagist/v/laragear/cache-query.svg)](https://packagist.org/packages/laragear/cache-query) [![Latest stable test run](https://github.com/Laragear/CacheQuery/workflows/Tests/badge.svg)](https://github.com/Laragear/CacheQuery/actions) [![Codecov coverage](https://codecov.io/gh/Laragear/CacheQuery/branch/1.x/graph/badge.svg?token=IOZS1TFJ5G)](https://codecov.io/gh/Laragear/CacheQuery) [![Maintainability](https://api.codeclimate.com/v1/badges/7e7894f3eee3939333eb/maintainability)](https://codeclimate.com/github/Laragear/CacheQuery/maintainability) [![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=Laragear_CacheQuery&metric=alert_status)](https://sonarcloud.io/dashboard?id=Laragear_CacheQuery) [![Laravel Octane Compatibility](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://laravel.com/docs/9.x/octane#introduction) +[![Latest Version on Packagist](https://img.shields.io/packagist/v/laragear/cache-query.svg)](https://packagist.org/packages/laragear/cache-query) +[![Latest stable test run](https://github.com/Laragear/CacheQuery/workflows/Tests/badge.svg)](https://github.com/Laragear/CacheQuery/actions) +[![Codecov coverage](https://codecov.io/gh/Laragear/CacheQuery/branch/1.x/graph/badge.svg?token=IOZS1TFJ5G)](https://codecov.io/gh/Laragear/CacheQuery) +[![Maintainability](https://api.codeclimate.com/v1/badges/7e7894f3eee3939333eb/maintainability)](https://codeclimate.com/github/Laragear/CacheQuery/maintainability) +[![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=Laragear_CacheQuery&metric=alert_status)](https://sonarcloud.io/dashboard?id=Laragear_CacheQuery) +[![Laravel Octane Compatibility](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://laravel.com/docs/9.x/octane#introduction) Remember your query results using only one method. Yes, only one. @@ -15,8 +20,8 @@ Your support allows me to keep this package free, up-to-date and maintainable. A ## Requirements -* PHP 8.0 -* Laravel 9.x +* PHP 8 or later +* Laravel 9, 10 or later ## Installation From 2bc036533639365f4ea3464a57e4114dad245479 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 12 Mar 2023 21:05:29 -0300 Subject: [PATCH 03/10] Fixes Laravel Copyright years. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1c9cbe..6025a88 100644 --- a/README.md +++ b/README.md @@ -274,4 +274,4 @@ If you discover any security related issues, please email darkghosthunter@gmail. This specific package version is licensed under the terms of the [MIT License](LICENSE.md), at time of publishing. -[Laravel](https://laravel.com) is a Trademark of [Taylor Otwell](https://github.com/TaylorOtwell/). Copyright © 2011-2022 Laravel LLC. +[Laravel](https://laravel.com) is a Trademark of [Taylor Otwell](https://github.com/TaylorOtwell/). Copyright © 2011-2023 Laravel LLC. From 29c0c8b5fdc7464827f504894c17eb8fd8df8319 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 12 Mar 2023 22:07:07 -0300 Subject: [PATCH 04/10] Minor clarifications [skip ci] --- README.md | 16 ++++++++++------ UPGRADE.md | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6025a88..586fac0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Codecov coverage](https://codecov.io/gh/Laragear/CacheQuery/branch/1.x/graph/badge.svg?token=IOZS1TFJ5G)](https://codecov.io/gh/Laragear/CacheQuery) [![Maintainability](https://api.codeclimate.com/v1/badges/7e7894f3eee3939333eb/maintainability)](https://codeclimate.com/github/Laragear/CacheQuery/maintainability) [![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=Laragear_CacheQuery&metric=alert_status)](https://sonarcloud.io/dashboard?id=Laragear_CacheQuery) -[![Laravel Octane Compatibility](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://laravel.com/docs/9.x/octane#introduction) +[![Laravel Octane Compatibility](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat&logo=laravel)](https://laravel.com/docs/10.x/octane#introduction) Remember your query results using only one method. Yes, only one. @@ -51,7 +51,7 @@ It's **eager load aware**. This means that it will cache an eager loaded relatio ```php use App\Models\User; -User::where('is_author')->with('posts')->cache()->paginate(); +$usersWithPosts = User::where('is_author')->with('posts')->cache()->paginate(); ``` ### Time-to-live @@ -82,7 +82,9 @@ Sometimes you may want to regenerate the results programmatically. To do that, s ```php use App\Models\Article; -Article::latest('published_at')->take(10)->cache(false)->get(); +$regen = request()->isNotFilled('no-cache'); + +Article::latest('published_at')->take(10)->cache($regen)->get(); ``` Finally, you can bypass the cache entirely using the query builder `when()` and `unless()` methods easily, as these are totally compatible with the `cache()` method. @@ -108,7 +110,9 @@ Article::latest('published_at')->take(10)->cache(store: 'redis')->get(); ### Cache Lock (data races) -On multiple processes, the query may be executed multiple times until the first process is able to store the result in the cache, specially when these take more than one second. To avoid this, set the `wait` parameter with the number of seconds to hold the acquired lock. +On multiple processes, the query may be executed multiple times until the first process is able to store the result in the cache, specially when these take more than one second. Take, for example, 1,000 users reading the latest 10 post of a site at the same time will call the database 100 times. + +To avoid this, set the `wait` parameter with the number of seconds to hold the acquired lock. ```php use App\Models\Article; @@ -224,13 +228,13 @@ User::query()->cache()->whereAge(20)->whereName('Joe')->first(); // Cache key: "cache-query|muDJevbVppCsTFcdeZBxsA==" ``` -To avoid this, ensure you always execute the same query, or centralize the query somewhere in your application (like using a [query scope](https://laravel.com/docs/9.x/eloquent#query-scopes)). +To avoid this, ensure you always execute the same query, or centralize the query somewhere in your application (like using a [query scope](https://laravel.com/docs/10.x/eloquent#query-scopes)). > **Note** This is by design. Ordering the query bindings would make operations commutative, but also disrupt [query-index optimizations](https://use-the-index-luke.com/sql/where-clause/the-equals-operator/concatenated-keys). Consider this not a bug, but a _feature_. ### Cannot delete autogenerated keys -All queries are cached using a BASE64 encoded MD5 hash of the connection name, SQL query and its bindings. This avoids any collision with other queries or different databases, while keeping the cache key shorter than the raw query for a faster lookup in the cache store. +All queries are cached using a BASE64 encoded MD5 hash of the connection name, SQL query and its bindings. This avoids any collision with other queries even from different databases, and also makes the cache lookup faster thanks to a shorter cache key. ```php User::query()->cache()->whereAge(20)->whereName('Joe')->first(); diff --git a/UPGRADE.md b/UPGRADE.md index 95685de..3bfcde9 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -4,20 +4,28 @@ ### Cache keys -Cache keys are now exclusively to delete one or multiple queries, like tags. Multiple queries using the same key will yield different results, as these will not share the same cache key anymore. +Cache keys are now used exclusively to delete one or multiple queries, like tags. Multiple queries using the same key will yield different results, as these will not share the same cache key anymore. If you need to keep the same functionality, use your application Cache directly. +```php +use App\Modesl\User; + +$users = cache()->remember('users', 60, function () { + return User::all(); +}) +``` + ## From 1.x ### Idempotent queries [Idempotent](https://en.wikipedia.org/wiki/Idempotence) queries have been removed. Cached queries only work for `SELECT` procedures, like `first()` or `get()`. -As an alternative, you can use `remember()` from your application cache for the same effect: +As an alternative, you can use the `remember()` method of your application cache for the same effect: ```php -cache()->remember('idempotent', function () { +cache()->remember('idempotent', 60, function () { Article::whereKey(10)->increment('unique_views'); return true; @@ -26,4 +34,4 @@ cache()->remember('idempotent', function () { ### Query Hash -If for some reason you where using the query hashes, 2.x incorporates the connection name into the hash. +If for some reason you where using the query hashes, 2.x incorporates the connection name into the hash. This means that the cached query will be usable only for the given connection. From 79348a04727518d43c6a85ee9947e05deb211c36 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 12 Mar 2023 22:46:18 -0300 Subject: [PATCH 05/10] Fixes typo --- .github/workflows/php.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 89cea85..1ea042e 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -53,7 +53,7 @@ jobs: - "8.0" - "8.1" - "8.2" - laravel-constraintt: + laravel-constraint: - "9.*" - "10.*" dependencies: From b5cb958dfba361bf1f6e9163f2e25146cab2bb84 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 21 May 2023 00:59:42 -0400 Subject: [PATCH 06/10] Updates PHPUnit config. --- phpunit.xml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 7b65982..9533052 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,5 @@ - - - - src/ - - - - - + tests @@ -20,4 +12,9 @@ + + + src/ + + From dc286c8e518d8a5b9b25546bad535c37a5ad76fa Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 21 May 2023 01:00:02 -0400 Subject: [PATCH 07/10] Fixes #18 --- src/CacheAwareConnectionProxy.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/CacheAwareConnectionProxy.php b/src/CacheAwareConnectionProxy.php index 63785c8..cbb4884 100644 --- a/src/CacheAwareConnectionProxy.php +++ b/src/CacheAwareConnectionProxy.php @@ -2,25 +2,26 @@ namespace Laragear\CacheQuery; -use function array_shift; -use function base64_encode; -use function cache; -use function config; use DateInterval; use DateTimeInterface; use Illuminate\Cache\NoLock; use Illuminate\Contracts\Cache\Lock; use Illuminate\Contracts\Cache\LockProvider; use Illuminate\Contracts\Cache\Repository; +use Illuminate\Database\Connection; use Illuminate\Database\ConnectionInterface; +use LogicException; +use function array_shift; +use function base64_encode; +use function cache; +use function config; use function implode; use function is_int; -use LogicException; use function max; use function md5; use function rtrim; -class CacheAwareConnectionProxy +class CacheAwareConnectionProxy extends Connection { /** * Create a new Cache Aware Connection Proxy instance. @@ -229,13 +230,13 @@ public function __set(string $name, mixed $value): void /** * Pass-through all method calls to the underlying connection. * - * @param string $name + * @param string $method * @param array $arguments * @return mixed */ - public function __call(string $name, array $arguments) + public function __call($method, $arguments) { - return $this->connection->{$name}(...$arguments); + return $this->connection->{$method}(...$arguments); } /** From 0f8da23bbd0a34706b01fb95bb13bb6bbdaf8d3f Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 21 May 2023 01:17:39 -0400 Subject: [PATCH 08/10] Adds test for using Group-By clause with pagination. --- tests/CacheAwareConnectionProxyTest.php | 38 +++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/tests/CacheAwareConnectionProxyTest.php b/tests/CacheAwareConnectionProxyTest.php index bbf3971..f5ba63b 100644 --- a/tests/CacheAwareConnectionProxyTest.php +++ b/tests/CacheAwareConnectionProxyTest.php @@ -268,6 +268,35 @@ public function test_cached_eloquent_query_hash_differs_when_pagination_changes_ static::assertSame(2, $second->firstItem()); } + public function test_caches_pagination_with_group_by(): void + { + User::query()->delete(); + + foreach (['first@bogus.com', 'second@bogus.com'] as $email) { + User::forceCreate([ + 'email' => $email, + 'name' => 'foo', + 'password' => 'password', + 'email_verified_at' => today(), + ]); + } + + User::query()->cache()->groupBy('name')->paginate(perPage: 1, page: 1); + User::query()->cache()->groupBy('name')->paginate(perPage: 1, page: 2); + + User::query()->delete(); + + $this->assertDatabaseEmpty('users'); + + $first = User::query()->cache()->groupBy('name')->paginate(perPage: 1, page: 1); + $second = User::query()->cache()->groupBy('name')->paginate(perPage: 1, page: 2); + + static::assertCount(1, $first->items()); + static::assertSame(1, $first->total()); + static::assertSame(1, $first->firstItem()); + static::assertEmpty($second); + } + public function test_uses_custom_time_to_live(): void { $hash = 'cache-query|30250dGAv64n2ySOIxuL+g'; @@ -622,12 +651,11 @@ public function test_no_exception_when_caching_eloquent_query_twice(): void public function test_pass_through_methods_to_wrapped_connection(): void { - $this->app->make('db')->table('users')->cache()->getConnection()->setDatabaseName('foo'); + $connection = $this->app->make('db')->table('users')->cache()->getConnection(); - static::assertSame( - 'foo', - $this->app->make('db')->table('users')->cache()->getConnection()->getDatabaseName('foo') - ); + $connection->setDatabaseName('foo'); + + static::assertSame('foo', $connection->getDatabaseName()); } public function test_pass_through_properties_set_and_get(): void From 67951091f1e9b1b2720b9107913aa0534f13bf02 Mon Sep 17 00:00:00 2001 From: Italo Date: Sun, 21 May 2023 05:18:00 +0000 Subject: [PATCH 09/10] Apply fixes from StyleCI [ci skip] [skip ci] --- src/CacheAwareConnectionProxy.php | 2 ++ src/CacheQueryServiceProvider.php | 1 + src/Console/Commands/CacheQuery/Forget.php | 5 +++-- tests/CacheAwareConnectionProxyTest.php | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CacheAwareConnectionProxy.php b/src/CacheAwareConnectionProxy.php index cbb4884..6e7d8da 100644 --- a/src/CacheAwareConnectionProxy.php +++ b/src/CacheAwareConnectionProxy.php @@ -11,6 +11,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\ConnectionInterface; use LogicException; + use function array_shift; use function base64_encode; use function cache; @@ -220,6 +221,7 @@ public function __get(string $name): mixed * @param string $name * @param mixed $value * @return void + * * @noinspection MagicMethodsValidityInspection */ public function __set(string $name, mixed $value): void diff --git a/src/CacheQueryServiceProvider.php b/src/CacheQueryServiceProvider.php index 563f96d..3d3f2a8 100644 --- a/src/CacheQueryServiceProvider.php +++ b/src/CacheQueryServiceProvider.php @@ -96,6 +96,7 @@ protected function eloquentMacro(): Closure ): EloquentBuilder { /** * @var \Illuminate\Database\Eloquent\Builder $this + * * @phpstan-ignore-next-line */ $this->getQuery()->cache($ttl, $key, $store, $wait); diff --git a/src/Console/Commands/CacheQuery/Forget.php b/src/Console/Commands/CacheQuery/Forget.php index 2894ffd..585d600 100644 --- a/src/Console/Commands/CacheQuery/Forget.php +++ b/src/Console/Commands/CacheQuery/Forget.php @@ -2,11 +2,12 @@ namespace Laragear\CacheQuery\Console\Commands\CacheQuery; -use function array_map; -use function explode; use Illuminate\Console\Command; use Laragear\CacheQuery\CacheQuery; +use function array_map; +use function explode; + class Forget extends Command { /** diff --git a/tests/CacheAwareConnectionProxyTest.php b/tests/CacheAwareConnectionProxyTest.php index f5ba63b..eb207e0 100644 --- a/tests/CacheAwareConnectionProxyTest.php +++ b/tests/CacheAwareConnectionProxyTest.php @@ -17,6 +17,7 @@ use Laragear\CacheQuery\CacheAwareConnectionProxy; use LogicException; use Mockery; + use function now; use function today; From fddb74a17b53a7dd58c370fae65a5a7ecc799aa0 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Sun, 21 May 2023 01:20:15 -0400 Subject: [PATCH 10/10] Fixes static analysis. --- src/CacheQueryServiceProvider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CacheQueryServiceProvider.php b/src/CacheQueryServiceProvider.php index 563f96d..cd83d27 100644 --- a/src/CacheQueryServiceProvider.php +++ b/src/CacheQueryServiceProvider.php @@ -72,7 +72,6 @@ protected function macro(): Closure $this->connection = $this->connection->connection; } - // @phpstan-ignore-next-line $this->connection = CacheAwareConnectionProxy::crateNewInstance( $this->connection, $ttl === false ? -1 : $ttl, $key, $wait, $store );