From 49370c70cc2b25d1b80252160ba3ab435828c95a Mon Sep 17 00:00:00 2001 From: korridor <26689068+korridor@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:05:39 +0100 Subject: [PATCH] Add option to include soft deleted models --- src/Rules/ExistsEloquent.php | 33 +++++++++++++ src/Rules/UniqueEloquent.php | 34 +++++++++++++ tests/Feature/ExistsEloquentTest.php | 73 ++++++++++++++++++++++++++++ tests/Feature/UniqueEloquentTest.php | 73 ++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) diff --git a/src/Rules/ExistsEloquent.php b/src/Rules/ExistsEloquent.php index 8ccd714..4759400 100644 --- a/src/Rules/ExistsEloquent.php +++ b/src/Rules/ExistsEloquent.php @@ -46,6 +46,13 @@ class ExistsEloquent implements ValidationRule */ private ?string $customMessageTranslationKey = null; + /** + * Include soft deleted models in the query. + * + * @var bool + */ + private bool $includeSoftDeleted = false; + /** * Create a new rule instance. * @@ -110,6 +117,10 @@ public function validate(string $attribute, mixed $value, Closure $fail): void $builder = $builderClosure($builder); } + if ($this->includeSoftDeleted) { + $builder = $builder->withTrashed(); + } + if ($builder->doesntExist()) { if ($this->customMessage !== null) { $fail($this->customMessage); @@ -141,4 +152,26 @@ public function query(Closure $builderClosure): self return $this; } + + /** + * Activate or deactivate including soft deleted models in the query. + * + * @param bool $includeSoftDeleted + * @return void + */ + public function setIncludeSoftDeleted(bool $includeSoftDeleted): void + { + $this->includeSoftDeleted = $includeSoftDeleted; + } + + /** + * Activate including soft deleted models in the query. + * @return $this + */ + public function includeSoftDeleted(): self + { + $this->setIncludeSoftDeleted(true); + + return $this; + } } diff --git a/src/Rules/UniqueEloquent.php b/src/Rules/UniqueEloquent.php index 3769f4a..8a74841 100644 --- a/src/Rules/UniqueEloquent.php +++ b/src/Rules/UniqueEloquent.php @@ -56,6 +56,13 @@ class UniqueEloquent implements ValidationRule */ private ?string $customMessageTranslationKey = null; + /** + * Include soft deleted models in the query. + * + * @var bool + */ + private bool $includeSoftDeleted = false; + /** * UniqueEloquent constructor. * @@ -97,6 +104,10 @@ public function validate(string $attribute, mixed $value, Closure $fail): void ); } + if ($this->includeSoftDeleted) { + $builder = $builder->withTrashed(); + } + if ($builder->exists()) { if ($this->customMessage !== null) { $fail($this->customMessage); @@ -178,4 +189,27 @@ public function ignore(mixed $id, ?string $column = null): self return $this; } + + /** + * Activate or deactivate including soft deleted models in the query. + * + * @param bool $includeSoftDeleted + * @return void + */ + public function setIncludeSoftDeleted(bool $includeSoftDeleted): void + { + $this->includeSoftDeleted = $includeSoftDeleted; + } + + /** + * Activate including soft deleted models in the query. + * + * @return $this + */ + public function includeSoftDeleted(): self + { + $this->setIncludeSoftDeleted(true); + + return $this; + } } diff --git a/tests/Feature/ExistsEloquentTest.php b/tests/Feature/ExistsEloquentTest.php index 6d3447e..cdb9bf3 100644 --- a/tests/Feature/ExistsEloquentTest.php +++ b/tests/Feature/ExistsEloquentTest.php @@ -255,6 +255,79 @@ public function testValidationPassesIfRuleChecksThatFactExistsAndBelongsToUserUs $this->assertCount(1, Fact::all()); } + /* + * Tests for includeSoftDeleted + */ + + public function testValidationSucceedsIfSoftDeletedEntryExistInDatabaseAndIncludeSoftDeletedFlagIsActive(): void + { + // Arrange + User::create([ + 'id' => 6, + 'other_id' => null, + 'name' => 'Testname', + 'email' => 'name@test.com', + 'password' => bcrypt('secret'), + 'remember_token' => Str::random(10), + ]); + $fact = Fact::create([ + 'id' => 1, + 'user_id' => 6, + 'type' => 'type1', + 'description' => 'Long desc', + ]); + $fact->delete(); + + $validator = Validator::make([ + 'id' => 1, + ], [ + 'id' => [(new ExistsEloquent(Fact::class))->includeSoftDeleted()] + ]); + + // Act + $isValid = $validator->passes(); + $messages = $validator->messages()->toArray(); + + // Assert + $this->assertTrue($isValid); + $this->assertArrayNotHasKey('id', $messages); + $this->assertCount(1, User::withTrashed()->get()); + $this->assertCount(1, User::all()); + $this->assertCount(1, Fact::withTrashed()->get()); + $this->assertCount(0, Fact::all()); + } + + public function testValidationFailsIfSoftDeletedEntryDoesNotExistInDatabaseAndIncludeSoftDeletedFlagIsActive(): void + { + // Arrange + User::create([ + 'id' => 6, + 'other_id' => null, + 'name' => 'Testname', + 'email' => 'name@test.com', + 'password' => bcrypt('secret'), + 'remember_token' => Str::random(10), + ]); + + $validator = Validator::make([ + 'id' => 1, + ], [ + 'id' => [(new ExistsEloquent(Fact::class))->includeSoftDeleted()] + ]); + + // Act + $isValid = $validator->passes(); + $messages = $validator->messages()->toArray(); + + // Assert + $this->assertFalse($isValid); + $this->assertEquals('The resource does not exist.', $messages['id'][0]); + $this->assertCount(1, User::withTrashed()->get()); + $this->assertCount(1, User::all()); + $this->assertCount(0, Fact::withTrashed()->get()); + $this->assertCount(0, Fact::all()); + } + /* * Test language support */ diff --git a/tests/Feature/UniqueEloquentTest.php b/tests/Feature/UniqueEloquentTest.php index 20766a8..1af2b9b 100644 --- a/tests/Feature/UniqueEloquentTest.php +++ b/tests/Feature/UniqueEloquentTest.php @@ -257,6 +257,79 @@ public function testValidationFailsIfRuleChecksThatFactExistsAndBelongsToUserUsi $this->assertCount(1, Fact::all()); } + /* + * Tests for includeSoftDeleted + */ + + public function testValidationSucceedsIfSoftDeletedEntryDoesNotExistInDatabaseAndIncludeSoftDeletedFlagIsActive(): void + { + // Arrange + User::create([ + 'id' => 6, + 'other_id' => null, + 'name' => 'Testname', + 'email' => 'name@test.com', + 'password' => bcrypt('secret'), + 'remember_token' => Str::random(10), + ]); + + $validator = Validator::make([ + 'id' => 1, + ], [ + 'id' => [(new UniqueEloquent(Fact::class))->includeSoftDeleted()] + ]); + + // Act + $isValid = $validator->passes(); + $messages = $validator->messages()->toArray(); + + // Assert + $this->assertTrue($isValid); + $this->assertArrayNotHasKey('id', $messages); + $this->assertCount(1, User::withTrashed()->get()); + $this->assertCount(1, User::all()); + $this->assertCount(0, Fact::withTrashed()->get()); + $this->assertCount(0, Fact::all()); + } + + public function testValidationFailsIfSoftDeletedEntryDoesExistInDatabaseAndIncludeSoftDeletedFlagIsActive(): void + { + // Arrange + User::create([ + 'id' => 6, + 'other_id' => null, + 'name' => 'Testname', + 'email' => 'name@test.com', + 'password' => bcrypt('secret'), + 'remember_token' => Str::random(10), + ]); + $fact = Fact::create([ + 'id' => 1, + 'user_id' => 6, + 'type' => 'type1', + 'description' => 'Long desc', + ]); + $fact->delete(); + + $validator = Validator::make([ + 'id' => 1, + ], [ + 'id' => [(new UniqueEloquent(Fact::class))->includeSoftDeleted()] + ]); + + // Act + $isValid = $validator->passes(); + $messages = $validator->messages()->toArray(); + + // Assert + $this->assertFalse($isValid); + $this->assertEquals('The resource already exists.', $messages['id'][0]); + $this->assertCount(1, User::withTrashed()->get()); + $this->assertCount(1, User::all()); + $this->assertCount(1, Fact::withTrashed()->get()); + $this->assertCount(0, Fact::all()); + } + /* * Test language support */