From 765c6cf75912744925d1f93b12e20bf6a41cc64f Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 01:11:13 -0700 Subject: [PATCH 01/12] Implement only and except logic --- src/Illuminate/Validation/Rules/Enum.php | 45 ++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index d66a16d126bc..ebfff9260052 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -4,7 +4,9 @@ use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\ValidatorAwareRule; +use Illuminate\Support\Arr; use TypeError; +use UnitEnum; class Enum implements Rule, ValidatorAwareRule { @@ -22,6 +24,16 @@ class Enum implements Rule, ValidatorAwareRule */ protected $validator; + /** + * Cases considered as valid + */ + private array $only = []; + + /** + * Cases considered as invalid + */ + private array $except = []; + /** * Create a new rule instance. * @@ -43,7 +55,7 @@ public function __construct($type) public function passes($attribute, $value) { if ($value instanceof $this->type) { - return true; + return $this->isDesirable($value); } if (is_null($value) || ! enum_exists($this->type) || ! method_exists($this->type, 'tryFrom')) { @@ -51,7 +63,7 @@ public function passes($attribute, $value) } try { - return ! is_null($this->type::tryFrom($value)); + return ! is_null($value = $this->type::tryFrom($value)) && $this->isDesirable($value); } catch (TypeError) { return false; } @@ -83,4 +95,33 @@ public function setValidator($validator) return $this; } + + /** + * Set specific cases to be valid + */ + public function only(array|UnitEnum $enums): static + { + $this->only = Arr::wrap($enums); + + return $this; + } + + /** + * Set specific cases to be invalid + */ + public function except(array|UnitEnum $enums): static + { + $this->except = Arr::wrap($enums); + + return $this; + } + + private function isDesirable(mixed $value): bool + { + return match (true) { + !empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), + !empty($this->except) => !in_array(needle: $value, haystack: $this->except, strict: true), + default => true, + }; + } } From 98fa8cb91e95fee9323925b5da4addb3445afff3 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 01:12:21 -0700 Subject: [PATCH 02/12] Cover only and except logic with tests --- tests/Validation/ValidationEnumRuleTest.php | 88 +++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/tests/Validation/ValidationEnumRuleTest.php b/tests/Validation/ValidationEnumRuleTest.php index 1c14c766f262..beffc1b314b1 100644 --- a/tests/Validation/ValidationEnumRuleTest.php +++ b/tests/Validation/ValidationEnumRuleTest.php @@ -78,6 +78,84 @@ public function testValidationFailsWhenProvidingNoExistingCases() $this->assertEquals(['The selected status is invalid.'], $v->messages()->get('status')); } + public function testValidationPassesForAllCasesUntilEitherOnlyOrExceptIsPassed() + { + $v = new Validator( + resolve('translator'), + [ + 'status_1' => PureEnum::one, + 'status_2' => PureEnum::two, + 'status_3' => IntegerStatus::done->value, + ], + [ + 'status_1' => new Enum(PureEnum::class), + 'status_2' => (new Enum(PureEnum::class))->only([])->except([]), + 'status_3' => new Enum(IntegerStatus::class), + ], + ); + + $this->assertTrue($v->passes()); + } + + /** + * @dataProvider conditionalCasesDataProvider + */ + public function testValidationPassesWhenOnlyCasesProvided( + IntegerStatus|int $enum, + array|IntegerStatus $only, + bool $expected + ) { + $v = new Validator( + resolve('translator'), + [ + 'status' => $enum, + ], + [ + 'status' => (new Enum(IntegerStatus::class))->only($only), + ], + ); + + $this->assertSame($expected, $v->passes()); + } + + /** + * @dataProvider conditionalCasesDataProvider + */ + public function testValidationPassesWhenExceptCasesProvided( + int|IntegerStatus $enum, + array|IntegerStatus $except, + bool $expected + ) { + $v = new Validator( + resolve('translator'), + [ + 'status' => $enum, + ], + [ + 'status' => (new Enum(IntegerStatus::class))->except($except), + ], + ); + + $this->assertSame($expected, $v->fails()); + } + + public function testOnlyHasHigherOrderThanExcept() + { + $v = new Validator( + resolve('translator'), + [ + 'status' => PureEnum::one, + ], + [ + 'status' => (new Enum(PureEnum::class)) + ->only(PureEnum::one) + ->except(PureEnum::one), + ], + ); + + $this->assertTrue($v->passes()); + } + public function testValidationFailsWhenProvidingDifferentType() { $v = new Validator( @@ -171,6 +249,16 @@ public function testValidationFailsWhenProvidingStringToIntegerType() $this->assertEquals(['The selected status is invalid.'], $v->messages()->get('status')); } + public static function conditionalCasesDataProvider(): array + { + return [ + [IntegerStatus::done, IntegerStatus::done, true], + [IntegerStatus::done, [IntegerStatus::done, IntegerStatus::pending], true], + [IntegerStatus::pending->value, [IntegerStatus::done, IntegerStatus::pending], true], + [IntegerStatus::done->value, IntegerStatus::pending, false], + ]; + } + protected function setUp(): void { $container = Container::getInstance(); From 1b3f917bf3ca0ec1d84735cf4f19b467871cb357 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 01:41:43 -0700 Subject: [PATCH 03/12] Fix code styling --- src/Illuminate/Validation/Rules/Enum.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index ebfff9260052..1df97c0ffcfc 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -25,12 +25,12 @@ class Enum implements Rule, ValidatorAwareRule protected $validator; /** - * Cases considered as valid + * Cases considered as valid. */ private array $only = []; /** - * Cases considered as invalid + * Cases considered as invalid. */ private array $except = []; @@ -97,7 +97,7 @@ public function setValidator($validator) } /** - * Set specific cases to be valid + * Set specific cases to be valid. */ public function only(array|UnitEnum $enums): static { @@ -107,7 +107,7 @@ public function only(array|UnitEnum $enums): static } /** - * Set specific cases to be invalid + * Set specific cases to be invalid. */ public function except(array|UnitEnum $enums): static { @@ -119,8 +119,8 @@ public function except(array|UnitEnum $enums): static private function isDesirable(mixed $value): bool { return match (true) { - !empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), - !empty($this->except) => !in_array(needle: $value, haystack: $this->except, strict: true), + ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), + ! empty($this->except) => !in_array(needle: $value, haystack: $this->except, strict: true), default => true, }; } From 90db20222ba44fd4eccc6c7bd7f1363c1f3c22f4 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 01:42:20 -0700 Subject: [PATCH 04/12] Fix code styling --- src/Illuminate/Validation/Rules/Enum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index 1df97c0ffcfc..418b0b2e075d 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -120,7 +120,7 @@ private function isDesirable(mixed $value): bool { return match (true) { ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), - ! empty($this->except) => !in_array(needle: $value, haystack: $this->except, strict: true), + ! empty($this->except) => ! in_array(needle: $value, haystack: $this->except, strict: true), default => true, }; } From dc79ddd45671b797dc95c1d7123577f75305ceae Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 14:53:15 -0700 Subject: [PATCH 05/12] Improve php doc --- src/Illuminate/Validation/Rules/Enum.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index 418b0b2e075d..aefa7dfd1f4d 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -98,6 +98,8 @@ public function setValidator($validator) /** * Set specific cases to be valid. + * + * @param UnitEnum[]|UnitEnum $enums */ public function only(array|UnitEnum $enums): static { @@ -108,6 +110,8 @@ public function only(array|UnitEnum $enums): static /** * Set specific cases to be invalid. + * + * @param UnitEnum[]|UnitEnum $enums */ public function except(array|UnitEnum $enums): static { From 4a84f8961bddb56daa5ad875092ddc0f2fe317f6 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 14:54:48 -0700 Subject: [PATCH 06/12] Fix code styling --- src/Illuminate/Validation/Rules/Enum.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index aefa7dfd1f4d..8002f3b4cbd6 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -99,7 +99,7 @@ public function setValidator($validator) /** * Set specific cases to be valid. * - * @param UnitEnum[]|UnitEnum $enums + * @param UnitEnum[]|UnitEnum $enums */ public function only(array|UnitEnum $enums): static { @@ -111,7 +111,7 @@ public function only(array|UnitEnum $enums): static /** * Set specific cases to be invalid. * - * @param UnitEnum[]|UnitEnum $enums + * @param UnitEnum[]|UnitEnum $enums */ public function except(array|UnitEnum $enums): static { From 2b378451b0175e34392754173af010efed7ffa94 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 24 Feb 2024 14:56:37 -0700 Subject: [PATCH 07/12] Fix code styling --- src/Illuminate/Validation/Rules/Enum.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index 8002f3b4cbd6..869febcf8d7b 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -99,7 +99,7 @@ public function setValidator($validator) /** * Set specific cases to be valid. * - * @param UnitEnum[]|UnitEnum $enums + * @param UnitEnum[]|UnitEnum $enums */ public function only(array|UnitEnum $enums): static { @@ -111,7 +111,7 @@ public function only(array|UnitEnum $enums): static /** * Set specific cases to be invalid. * - * @param UnitEnum[]|UnitEnum $enums + * @param UnitEnum[]|UnitEnum $enums */ public function except(array|UnitEnum $enums): static { From 4d1f8bdb0d688426e32a36c1cbc683c016c4132f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 08:24:22 -0600 Subject: [PATCH 08/12] formatting --- src/Illuminate/Validation/Rules/Enum.php | 77 ++++++++++++++---------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index 869febcf8d7b..c3706d0e985c 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -6,7 +6,6 @@ use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Support\Arr; use TypeError; -use UnitEnum; class Enum implements Rule, ValidatorAwareRule { @@ -25,12 +24,16 @@ class Enum implements Rule, ValidatorAwareRule protected $validator; /** - * Cases considered as valid. + * The cases that should be considered valid. + * + * @var array */ private array $only = []; /** - * Cases considered as invalid. + * The cases that should be considered invalid. + * + * @var array */ private array $except = []; @@ -63,69 +66,77 @@ public function passes($attribute, $value) } try { - return ! is_null($value = $this->type::tryFrom($value)) && $this->isDesirable($value); + $value = $this->type::tryFrom($value); + + return ! is_null($value) && $this->isDesirable($value); } catch (TypeError) { return false; } } /** - * Get the validation error message. + * Specify the cases that should be considered valid. * - * @return array + * @param \UnitEnum[]|\UnitEnum $values */ - public function message() + public function only($values): static { - $message = $this->validator->getTranslator()->get('validation.enum'); + $this->only = Arr::wrap($values); - return $message === 'validation.enum' - ? ['The selected :attribute is invalid.'] - : $message; + return $this; } /** - * Set the current validator. + * Specify the cases that should be considered invalid. * - * @param \Illuminate\Validation\Validator $validator - * @return $this + * @param \UnitEnum[]|\UnitEnum $values */ - public function setValidator($validator) + public function except($values): static { - $this->validator = $validator; + $this->except = Arr::wrap($values); return $this; } /** - * Set specific cases to be valid. + * Determine if the given case is a valid case based on the only / except values. * - * @param UnitEnum[]|UnitEnum $enums + * @param mixed $value + * @return bool */ - public function only(array|UnitEnum $enums): static + private function isDesirable(mixed $value): bool { - $this->only = Arr::wrap($enums); - - return $this; + return match (true) { + ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), + ! empty($this->except) => ! in_array(needle: $value, haystack: $this->except, strict: true), + default => true, + }; } /** - * Set specific cases to be invalid. + * Get the validation error message. * - * @param UnitEnum[]|UnitEnum $enums + * @return array */ - public function except(array|UnitEnum $enums): static + public function message() { - $this->except = Arr::wrap($enums); + $message = $this->validator->getTranslator()->get('validation.enum'); - return $this; + return $message === 'validation.enum' + ? ['The selected :attribute is invalid.'] + : $message; } - private function isDesirable(mixed $value): bool + /** + * Set the current validator. + * + * @param \Illuminate\Validation\Validator $validator + * @return $this + */ + public function setValidator($validator) { - return match (true) { - ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), - ! empty($this->except) => ! in_array(needle: $value, haystack: $this->except, strict: true), - default => true, - }; + $this->validator = $validator; + + return $this; } } From 0a099818da1ca82ad2c2429623cd9291499a22a6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 08:24:40 -0600 Subject: [PATCH 09/12] fix visibility --- src/Illuminate/Validation/Rules/Enum.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index c3706d0e985c..83cc95034a07 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -28,14 +28,14 @@ class Enum implements Rule, ValidatorAwareRule * * @var array */ - private array $only = []; + protected array $only = []; /** * The cases that should be considered invalid. * * @var array */ - private array $except = []; + protected array $except = []; /** * Create a new rule instance. @@ -104,7 +104,7 @@ public function except($values): static * @param mixed $value * @return bool */ - private function isDesirable(mixed $value): bool + protected function isDesirable(mixed $value): bool { return match (true) { ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), From c6144d4ba6389c328709928b9de08be8043eafd5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 08:25:05 -0600 Subject: [PATCH 10/12] fix type hints --- src/Illuminate/Validation/Rules/Enum.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index 83cc95034a07..dd5ac5650b5b 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -28,14 +28,14 @@ class Enum implements Rule, ValidatorAwareRule * * @var array */ - protected array $only = []; + protected $only = []; /** * The cases that should be considered invalid. * * @var array */ - protected array $except = []; + protected $except = []; /** * Create a new rule instance. @@ -104,7 +104,7 @@ public function except($values): static * @param mixed $value * @return bool */ - protected function isDesirable(mixed $value): bool + protected function isDesirable(mixed $value) { return match (true) { ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), From d709c48964aa4e2f165d7fd647db1b7aa2b34700 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 08:25:23 -0600 Subject: [PATCH 11/12] remove type hint --- src/Illuminate/Validation/Rules/Enum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index dd5ac5650b5b..9cd28d06e644 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -104,7 +104,7 @@ public function except($values): static * @param mixed $value * @return bool */ - protected function isDesirable(mixed $value) + protected function isDesirable($value) { return match (true) { ! empty($this->only) => in_array(needle: $value, haystack: $this->only, strict: true), From e8a951ce40292461316723125d609aaf430b5aee Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 25 Feb 2024 08:25:57 -0600 Subject: [PATCH 12/12] fix type hints --- src/Illuminate/Validation/Rules/Enum.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Validation/Rules/Enum.php b/src/Illuminate/Validation/Rules/Enum.php index 9cd28d06e644..42d9e6f012d2 100644 --- a/src/Illuminate/Validation/Rules/Enum.php +++ b/src/Illuminate/Validation/Rules/Enum.php @@ -78,8 +78,9 @@ public function passes($attribute, $value) * Specify the cases that should be considered valid. * * @param \UnitEnum[]|\UnitEnum $values + * @return $this */ - public function only($values): static + public function only($values) { $this->only = Arr::wrap($values); @@ -90,8 +91,9 @@ public function only($values): static * Specify the cases that should be considered invalid. * * @param \UnitEnum[]|\UnitEnum $values + * @return $this */ - public function except($values): static + public function except($values) { $this->except = Arr::wrap($values);