From 10bee98a65efb0de68131f0b4fd9eb9c2be9d8df Mon Sep 17 00:00:00 2001 From: mostafaznv Date: Mon, 21 Oct 2024 16:56:55 +0330 Subject: [PATCH 1/4] chore: improve IDE support for whereUuid scope --- src/GeneratesUuid.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GeneratesUuid.php b/src/GeneratesUuid.php index 7b47fdb..70cb70e 100644 --- a/src/GeneratesUuid.php +++ b/src/GeneratesUuid.php @@ -23,7 +23,7 @@ * * @property string $uuidVersion * - * @method static \Illuminate\Database\Eloquent\Builder whereUuid(string $uuid) + * @method static \Illuminate\Database\Eloquent\Builder whereUuid(string|string[] $uuid, ?string $uuidColumn = null) */ trait GeneratesUuid { From 17702a2ff1f03039254c90d392f0f6064fce5ebd Mon Sep 17 00:00:00 2001 From: mostafaznv Date: Mon, 21 Oct 2024 16:58:13 +0330 Subject: [PATCH 2/4] feat: add whereNotUuid global scope --- src/GeneratesUuid.php | 51 +++++++++++++++++ tests/Feature/UuidTest.php | 109 +++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/src/GeneratesUuid.php b/src/GeneratesUuid.php index 70cb70e..e1726ff 100644 --- a/src/GeneratesUuid.php +++ b/src/GeneratesUuid.php @@ -24,6 +24,7 @@ * @property string $uuidVersion * * @method static \Illuminate\Database\Eloquent\Builder whereUuid(string|string[] $uuid, ?string $uuidColumn = null) + * @method static \Illuminate\Database\Eloquent\Builder whereNotUuid(string|string[] $uuid, ?string $uuidColumn = null) */ trait GeneratesUuid { @@ -137,6 +138,24 @@ public function scopeWhereUuid($query, $uuid, $uuidColumn = null): Builder ); } + /** + * Scope queries to find by UUID. + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param string|array $uuid + * @param string $uuidColumn + */ + public function scopeWhereNotUuid($query, $uuid, $uuidColumn = null): Builder + { + $uuidColumn = $this->guessUuidColumn($uuidColumn); + $uuid = $this->prepareUuid($uuid, $uuidColumn); + + return $query->whereNotIn( + $this->qualifyColumn($uuidColumn), + Arr::wrap($uuid) + ); + } + /** * Convert a single UUID or array of UUIDs to bytes. * @@ -172,4 +191,36 @@ protected function normaliseUuids($uuid): array return $uuid; } + + + /** + * Guess UUID column based on model configurations or given uuid column + * + * @param ?string $uuidColumn + * @return string + */ + protected function guessUuidColumn($uuidColumn = null): string + { + return ! is_null($uuidColumn) && in_array($uuidColumn, $this->uuidColumns()) + ? $uuidColumn + : $this->uuidColumns()[0]; + } + + /** + * Prepare UUID by normalization + * + * @param string|array $uuid + * @param string $uuidColumn + * @return string|array + */ + protected function prepareUuid($uuid, $uuidColumn): array|string + { + $uuid = $this->normaliseUuids($uuid); + + if ($this->isClassCastable($uuidColumn)) { + $uuid = $this->bytesFromUuid($uuid); + } + + return $uuid; + } } diff --git a/tests/Feature/UuidTest.php b/tests/Feature/UuidTest.php index 4d63d63..3cb9e4c 100644 --- a/tests/Feature/UuidTest.php +++ b/tests/Feature/UuidTest.php @@ -55,6 +55,18 @@ public function you_can_find_a_model_by_its_uuid() $this->assertSame($uuid, $post->uuid); } + /** @test */ + public function you_can_exclude_a_model_by_its_uuid() + { + $uuid = '55635d83-10bc-424f-bf3f-395ea7a5b47f'; + + Post::create(['title' => 'test post', 'uuid' => $uuid]); + + $this->assertNull( + Post::whereNotUuid($uuid)->first() + ); + } + /** @test */ public function you_can_find_a_model_by_custom_uuid_parameter() { @@ -88,6 +100,24 @@ public function you_can_search_by_array_of_uuids() ])->count()); } + /** @test */ + public function you_can_exclude_by_array_of_uuids() + { + Post::create(['title' => 'first post', 'uuid' => '8ab48e77-d9cd-4fe7-ace5-a5a428590c18']); + Post::create(['title' => 'second post', 'uuid' => 'c7c26456-ddb0-45cd-9b1c-318296cce7a3']); + Post::create(['title' => 'third post', 'uuid' => 'e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d']); + + $uuids = [ + '8ab48e77-d9cd-4fe7-ace5-A5A428590C18', + 'c7c26456-ddb0-45cd-9b1c-318296cce7a3', + ]; + + $posts = Post::whereNotUuid($uuids)->get(); + + $this->assertEquals(1, $posts->count()); + $this->assertEquals('e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d', $posts->get(0)->uuid); + } + /** @test */ public function you_can_search_by_array_of_efficient_uuids() { @@ -100,6 +130,24 @@ public function you_can_search_by_array_of_efficient_uuids() ], 'efficient_uuid')->count()); } + /** @test */ + public function you_can_exclude_by_array_of_efficient_uuids() + { + EfficientUuidPost::create(['title' => 'first post', 'efficient_uuid' => '8ab48e77-d9cd-4fe7-ace5-a5a428590c18']); + EfficientUuidPost::create(['title' => 'second post', 'efficient_uuid' => 'c7c26456-ddb0-45cd-9b1c-318296cce7a3']); + EfficientUuidPost::create(['title' => 'third post', 'efficient_uuid' => 'e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d']); + + $uuids = [ + '8ab48e77-d9cd-4fe7-ace5-A5A428590C18', + 'c7c26456-ddb0-45cd-9b1c-318296cce7a3', + ]; + + $posts = EfficientUuidPost::whereNotUuid($uuids, 'efficient_uuid')->get(); + + $this->assertEquals(1, $posts->count()); + $this->assertSame('e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d', $posts->get(0)->efficient_uuid); + } + /** @test */ public function you_can_search_by_array_of_uuids_for_custom_column() { @@ -112,6 +160,24 @@ public function you_can_search_by_array_of_uuids_for_custom_column() ], 'custom_uuid')->count()); } + /** @test */ + public function you_can_exclude_by_array_of_uuids_for_custom_column() + { + CustomCastUuidPost::create(['title' => 'first post', 'custom_uuid' => '8ab48e77-d9cd-4fe7-ace5-a5a428590c18']); + CustomCastUuidPost::create(['title' => 'second post', 'custom_uuid' => 'c7c26456-ddb0-45cd-9b1c-318296cce7a3']); + CustomCastUuidPost::create(['title' => 'third post', 'custom_uuid' => 'e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d']); + + $uuids = [ + '8ab48e77-d9cd-4fe7-ace5-A5A428590C18', + 'c7c26456-ddb0-45cd-9b1c-318296cce7a3', + ]; + + $posts = CustomCastUuidPost::whereNotUuid($uuids, 'custom_uuid')->get(); + + $this->assertEquals(1, $posts->count()); + $this->assertSame('e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d', $posts->get(0)->custom_uuid); + } + /** @test */ public function you_can_search_by_array_of_uuids_which_contains_an_invalid_uuid() { @@ -125,6 +191,25 @@ public function you_can_search_by_array_of_uuids_which_contains_an_invalid_uuid( ])->count()); } + /** @test */ + public function you_can_exclude_by_array_of_uuids_which_contains_an_invalid_uuid() + { + Post::create(['title' => 'first post', 'uuid' => '8ab48e77-d9cd-4fe7-ace5-a5a428590c18']); + Post::create(['title' => 'second post', 'uuid' => 'c7c26456-ddb0-45cd-9b1c-318296cce7a3']); + Post::create(['title' => 'third post', 'uuid' => 'e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d']); + + $uuids = [ + '8ab48e77-d9cd-4fe7-ace5-A5A428590C18', + 'c7c26456-ddb0-45cd-9b1c-318296cce7a3', + 'this is invalid', + ]; + + $posts = Post::whereNotUuid($uuids)->get(); + + $this->assertEquals(1, $posts->count()); + $this->assertEquals('e99d440e-fa25-45f2-ba2f-7c4c48f6fb5d', $posts->get(0)->uuid); + } + /** @test */ public function you_can_generate_a_uuid_without_casting() { @@ -164,6 +249,18 @@ public function you_can_find_a_model_by_uuid_without_casting() $this->assertSame($uuid, $post->uuid); } + /** @test */ + public function you_can_exclude_a_model_by_uuid_without_casting() + { + $uuid = 'b270f651-4db8-407b-aade-8666aca2750e'; + + UncastPost::create(['title' => 'test-post', 'uuid' => $uuid]); + + $post = UncastPost::whereNotUuid($uuid)->first(); + + $this->assertNull($post); + } + /** @test */ public function you_can_find_a_model_by_uuid_with_casting() { @@ -177,6 +274,18 @@ public function you_can_find_a_model_by_uuid_with_casting() $this->assertSame($uuid, $post->uuid); } + /** @test */ + public function you_can_exclude_a_model_by_uuid_with_casting() + { + $uuid = 'b270f651-4db8-407b-aade-8666aca2750e'; + + EfficientUuidPost::create(['title' => 'efficient uuid', 'uuid' => $uuid]); + + $post = EfficientUuidPost::whereNotUuid($uuid)->first(); + + $this->assertNull($post); + } + /** @test */ public function it_handles_time_ordered_uuids() { From a0e81f85d8af6ba74d662502fef086b2e4cdb1f8 Mon Sep 17 00:00:00 2001 From: mostafaznv Date: Mon, 21 Oct 2024 17:15:49 +0330 Subject: [PATCH 3/4] refactor: decouple UUID column guessing and UUID value preparation for reuse in additional scopes --- src/GeneratesUuid.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/GeneratesUuid.php b/src/GeneratesUuid.php index e1726ff..530fb9d 100644 --- a/src/GeneratesUuid.php +++ b/src/GeneratesUuid.php @@ -122,15 +122,8 @@ public function resolveUuidVersion(): string */ public function scopeWhereUuid($query, $uuid, $uuidColumn = null): Builder { - $uuidColumn = ! is_null($uuidColumn) && in_array($uuidColumn, $this->uuidColumns()) - ? $uuidColumn - : $this->uuidColumns()[0]; - - $uuid = $this->normaliseUuids($uuid); - - if ($this->isClassCastable($uuidColumn)) { - $uuid = $this->bytesFromUuid($uuid); - } + $uuidColumn = $this->guessUuidColumn($uuidColumn); + $uuid = $this->prepareUuid($uuid, $uuidColumn); return $query->whereIn( $this->qualifyColumn($uuidColumn), From 75a42fa38774098fd6212684b2215e9cc308711a Mon Sep 17 00:00:00 2001 From: Michael Dyrynda Date: Tue, 7 Jan 2025 10:42:14 +1030 Subject: [PATCH 4/4] rename method consistent with other eloquent method names --- src/GeneratesUuid.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/GeneratesUuid.php b/src/GeneratesUuid.php index 530fb9d..a664bd4 100644 --- a/src/GeneratesUuid.php +++ b/src/GeneratesUuid.php @@ -122,7 +122,7 @@ public function resolveUuidVersion(): string */ public function scopeWhereUuid($query, $uuid, $uuidColumn = null): Builder { - $uuidColumn = $this->guessUuidColumn($uuidColumn); + $uuidColumn = $this->getUuidColumn($uuidColumn); $uuid = $this->prepareUuid($uuid, $uuidColumn); return $query->whereIn( @@ -140,7 +140,7 @@ public function scopeWhereUuid($query, $uuid, $uuidColumn = null): Builder */ public function scopeWhereNotUuid($query, $uuid, $uuidColumn = null): Builder { - $uuidColumn = $this->guessUuidColumn($uuidColumn); + $uuidColumn = $this->getUuidColumn($uuidColumn); $uuid = $this->prepareUuid($uuid, $uuidColumn); return $query->whereNotIn( @@ -185,14 +185,12 @@ protected function normaliseUuids($uuid): array return $uuid; } - /** * Guess UUID column based on model configurations or given uuid column * * @param ?string $uuidColumn - * @return string */ - protected function guessUuidColumn($uuidColumn = null): string + protected function getUuidColumn($uuidColumn = null): string { return ! is_null($uuidColumn) && in_array($uuidColumn, $this->uuidColumns()) ? $uuidColumn @@ -204,7 +202,6 @@ protected function guessUuidColumn($uuidColumn = null): string * * @param string|array $uuid * @param string $uuidColumn - * @return string|array */ protected function prepareUuid($uuid, $uuidColumn): array|string {