From 3dea1f112b65c681324c8032fafc04295c057204 Mon Sep 17 00:00:00 2001 From: przemyslaw-przylucki Date: Sun, 5 May 2024 18:27:04 +0200 Subject: [PATCH 1/5] Multiple validation messages --- src/Support/StringHelper.php | 21 +++++ .../Exceptions/ValidationException.php | 4 +- src/Validation/Rule.php | 5 +- src/Validation/Rules/Password.php | 17 +--- tests/Unit/Validation/Rules/PasswordTest.php | 22 ++++-- .../Validation/ValidationExceptionTest.php | 79 +++++++++++++++++++ 6 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 src/Support/StringHelper.php create mode 100644 tests/Unit/Validation/ValidationExceptionTest.php diff --git a/src/Support/StringHelper.php b/src/Support/StringHelper.php new file mode 100644 index 0000000..d82b7ff --- /dev/null +++ b/src/Support/StringHelper.php @@ -0,0 +1,21 @@ + $failingRulesForField) { /** @var Rule $failingRuleForField */ foreach ($failingRulesForField as $failingRuleForField) { - $messages[$field][] = $failingRuleForField->message(); + $messages[$field][] = "Value should " . StringHelper::join(ArrayHelper::wrap($failingRuleForField->message())); } } diff --git a/src/Validation/Rule.php b/src/Validation/Rule.php index 62a51b9..f4dd96a 100644 --- a/src/Validation/Rule.php +++ b/src/Validation/Rule.php @@ -8,5 +8,8 @@ interface Rule { public function isValid(mixed $value): bool; - public function message(): string; + /** + * @return string|string[] + */ + public function message(): string|array; } diff --git a/src/Validation/Rules/Password.php b/src/Validation/Rules/Password.php index 10bc4d4..dd6f600 100644 --- a/src/Validation/Rules/Password.php +++ b/src/Validation/Rules/Password.php @@ -6,6 +6,7 @@ use Attribute; use Tempest\Validation\Rule; +use Tempest\Support\StringHelper; #[Attribute] final readonly class Password implements Rule @@ -51,9 +52,9 @@ public function isValid(mixed $value): bool return true; } - public function message(): string + public function message(): array { - $messages = ["at least {$this->min} characters"]; + $messages = ["contain at least {$this->min} characters"]; if ($this->mixedCase) { $messages[] = 'at least one uppercase and one lowercase letter'; @@ -68,16 +69,6 @@ public function message(): string $messages[] = 'at least one symbol'; } - return 'Value should contain ' . $this->natural_language_join($messages); - } - - private function natural_language_join(array $list) - { - $last = array_pop($list); - if ($list) { - return implode(', ', $list) . ' ' . 'and' . ' ' . $last; - } - - return $last; + return $messages; } } diff --git a/tests/Unit/Validation/Rules/PasswordTest.php b/tests/Unit/Validation/Rules/PasswordTest.php index fb73bf8..0167349 100644 --- a/tests/Unit/Validation/Rules/PasswordTest.php +++ b/tests/Unit/Validation/Rules/PasswordTest.php @@ -69,24 +69,32 @@ public function test_symbols() public function test_message() { $rule = new Password(); - $this->assertSame('Value should contain at least 12 characters', $rule->message()); + $this->assertSame('contain at least 12 characters', $rule->message()[0]); $rule = new Password(min: 4); - $this->assertSame('Value should contain at least 4 characters', $rule->message()); + $this->assertSame('contain at least 4 characters', $rule->message()[0]); $rule = new Password(mixedCase: true); - $this->assertSame('Value should contain at least 12 characters and at least one uppercase and one lowercase letter', $rule->message()); + $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('at least one uppercase and one lowercase letter', $rule->message()[1]); $rule = new Password(letters: true); - $this->assertSame('Value should contain at least 12 characters and at least one letter', $rule->message()); + $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('at least one letter', $rule->message()[1]); $rule = new Password(numbers: true); - $this->assertSame('Value should contain at least 12 characters and at least one number', $rule->message()); + $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('at least one number', $rule->message()[1]); $rule = new Password(symbols: true); - $this->assertSame('Value should contain at least 12 characters and at least one symbol', $rule->message()); + $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('at least one symbol', $rule->message()[1]); $rule = new Password(min: 4, mixedCase: true, letters: true, numbers: true, symbols: true); - $this->assertSame('Value should contain at least 4 characters, at least one uppercase and one lowercase letter, at least one number, at least one letter and at least one symbol', $rule->message()); + $this->assertSame('contain at least 4 characters', $rule->message()[0]); + $this->assertSame('at least one uppercase and one lowercase letter', $rule->message()[1]); + $this->assertSame('at least one number', $rule->message()[2]); + $this->assertSame('at least one letter', $rule->message()[3]); + $this->assertSame('at least one symbol', $rule->message()[4]); } } diff --git a/tests/Unit/Validation/ValidationExceptionTest.php b/tests/Unit/Validation/ValidationExceptionTest.php new file mode 100644 index 0000000..62b1aa1 --- /dev/null +++ b/tests/Unit/Validation/ValidationExceptionTest.php @@ -0,0 +1,79 @@ +expectException(ValidationException::class); + + $this->expectExceptionMessage('Value should be a valid email address'); + + throw new ValidationException(new stdClass(), [ + 'email' => [ + new class implements Rule { + + public function isValid(mixed $value): bool + { + return false; + } + + public function message(): string|array + { + return 'Value should be a valid email address'; + } + } + ] + ]); + } + + public function test_exception_message_with_multiple_messages(): void + { + $this->expectException(ValidationException::class); + + $this->expectExceptionMessage('Value should be a valid email address'); + $this->expectExceptionMessage("Value should praise tempest, old gods from the past and the new gods from the future"); + + throw new ValidationException(new stdClass(), [ + 'email' => [ + new class implements Rule { + + public function isValid(mixed $value): bool + { + return false; + } + + public function message(): string|array + { + return 'be a valid email address'; + } + }, + new class implements Rule { + + public function isValid(mixed $value): bool + { + return false; + } + + public function message(): string|array + { + return [ + 'praise tempest', + 'old gods from the past', + 'the new gods from the future', + ]; + } + }] + ]); + } + +} From 0caf5766c9950aca88c31ace9e59b1b2ae184476 Mon Sep 17 00:00:00 2001 From: przemyslaw-przylucki Date: Sun, 5 May 2024 18:29:34 +0200 Subject: [PATCH 2/5] x --- src/Validation/Exceptions/ValidationException.php | 2 +- src/Validation/Rules/Password.php | 2 +- tests/Unit/Validation/Rules/PasswordTest.php | 14 +++++++------- tests/Unit/Validation/ValidationExceptionTest.php | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Validation/Exceptions/ValidationException.php b/src/Validation/Exceptions/ValidationException.php index e1b208e..4e1fab9 100644 --- a/src/Validation/Exceptions/ValidationException.php +++ b/src/Validation/Exceptions/ValidationException.php @@ -22,7 +22,7 @@ public function __construct(object $object, array $failingRules) foreach ($failingRules as $field => $failingRulesForField) { /** @var Rule $failingRuleForField */ foreach ($failingRulesForField as $failingRuleForField) { - $messages[$field][] = "Value should " . StringHelper::join(ArrayHelper::wrap($failingRuleForField->message())); + $messages[$field][] = StringHelper::join(ArrayHelper::wrap($failingRuleForField->message())); } } diff --git a/src/Validation/Rules/Password.php b/src/Validation/Rules/Password.php index dd6f600..7264a82 100644 --- a/src/Validation/Rules/Password.php +++ b/src/Validation/Rules/Password.php @@ -54,7 +54,7 @@ public function isValid(mixed $value): bool public function message(): array { - $messages = ["contain at least {$this->min} characters"]; + $messages = ["Value should contain at least {$this->min} characters"]; if ($this->mixedCase) { $messages[] = 'at least one uppercase and one lowercase letter'; diff --git a/tests/Unit/Validation/Rules/PasswordTest.php b/tests/Unit/Validation/Rules/PasswordTest.php index 0167349..4b482bb 100644 --- a/tests/Unit/Validation/Rules/PasswordTest.php +++ b/tests/Unit/Validation/Rules/PasswordTest.php @@ -69,29 +69,29 @@ public function test_symbols() public function test_message() { $rule = new Password(); - $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 12 characters', $rule->message()[0]); $rule = new Password(min: 4); - $this->assertSame('contain at least 4 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 4 characters', $rule->message()[0]); $rule = new Password(mixedCase: true); - $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 12 characters', $rule->message()[0]); $this->assertSame('at least one uppercase and one lowercase letter', $rule->message()[1]); $rule = new Password(letters: true); - $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 12 characters', $rule->message()[0]); $this->assertSame('at least one letter', $rule->message()[1]); $rule = new Password(numbers: true); - $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 12 characters', $rule->message()[0]); $this->assertSame('at least one number', $rule->message()[1]); $rule = new Password(symbols: true); - $this->assertSame('contain at least 12 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 12 characters', $rule->message()[0]); $this->assertSame('at least one symbol', $rule->message()[1]); $rule = new Password(min: 4, mixedCase: true, letters: true, numbers: true, symbols: true); - $this->assertSame('contain at least 4 characters', $rule->message()[0]); + $this->assertSame('Value should contain at least 4 characters', $rule->message()[0]); $this->assertSame('at least one uppercase and one lowercase letter', $rule->message()[1]); $this->assertSame('at least one number', $rule->message()[2]); $this->assertSame('at least one letter', $rule->message()[3]); diff --git a/tests/Unit/Validation/ValidationExceptionTest.php b/tests/Unit/Validation/ValidationExceptionTest.php index 62b1aa1..4333428 100644 --- a/tests/Unit/Validation/ValidationExceptionTest.php +++ b/tests/Unit/Validation/ValidationExceptionTest.php @@ -54,7 +54,7 @@ public function isValid(mixed $value): bool public function message(): string|array { - return 'be a valid email address'; + return 'Value should be a valid email address'; } }, new class implements Rule { @@ -67,7 +67,7 @@ public function isValid(mixed $value): bool public function message(): string|array { return [ - 'praise tempest', + 'Value should praise tempest', 'old gods from the past', 'the new gods from the future', ]; From bfc8c486268fdefacfc9b7fe85d4442ba1c4c3b0 Mon Sep 17 00:00:00 2001 From: przemyslaw-przylucki Date: Sun, 5 May 2024 18:30:41 +0200 Subject: [PATCH 3/5] x --- src/Support/StringHelper.php | 2 +- src/Validation/Exceptions/ValidationException.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Support/StringHelper.php b/src/Support/StringHelper.php index d82b7ff..209adf9 100644 --- a/src/Support/StringHelper.php +++ b/src/Support/StringHelper.php @@ -7,7 +7,7 @@ final class StringHelper { - public static function join(array $strings): string + public static function naturalLangJoin(array $strings): string { $last = array_pop($strings); diff --git a/src/Validation/Exceptions/ValidationException.php b/src/Validation/Exceptions/ValidationException.php index 4e1fab9..6a34b77 100644 --- a/src/Validation/Exceptions/ValidationException.php +++ b/src/Validation/Exceptions/ValidationException.php @@ -22,7 +22,7 @@ public function __construct(object $object, array $failingRules) foreach ($failingRules as $field => $failingRulesForField) { /** @var Rule $failingRuleForField */ foreach ($failingRulesForField as $failingRuleForField) { - $messages[$field][] = StringHelper::join(ArrayHelper::wrap($failingRuleForField->message())); + $messages[$field][] = StringHelper::naturalLangJoin(ArrayHelper::wrap($failingRuleForField->message())); } } From 428c85f39beafe76f17eb950dbb2215aea892d59 Mon Sep 17 00:00:00 2001 From: przemyslaw-przylucki Date: Thu, 30 May 2024 22:09:38 +0200 Subject: [PATCH 4/5] x --- src/Support/{StringHelper.php => LanguageHelper.php} | 4 ++-- src/Validation/Exceptions/ValidationException.php | 4 ++-- src/Validation/Rules/Password.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Support/{StringHelper.php => LanguageHelper.php} (72%) diff --git a/src/Support/StringHelper.php b/src/Support/LanguageHelper.php similarity index 72% rename from src/Support/StringHelper.php rename to src/Support/LanguageHelper.php index 209adf9..bfc7aa2 100644 --- a/src/Support/StringHelper.php +++ b/src/Support/LanguageHelper.php @@ -4,10 +4,10 @@ namespace Tempest\Support; -final class StringHelper +final class LanguageHelper { - public static function naturalLangJoin(array $strings): string + public static function join(array $strings): string { $last = array_pop($strings); diff --git a/src/Validation/Exceptions/ValidationException.php b/src/Validation/Exceptions/ValidationException.php index 6a34b77..5917d57 100644 --- a/src/Validation/Exceptions/ValidationException.php +++ b/src/Validation/Exceptions/ValidationException.php @@ -7,7 +7,7 @@ use Exception; use Tempest\Validation\Rule; use Tempest\Support\ArrayHelper; -use Tempest\Support\StringHelper; +use Tempest\Support\LanguageHelper; final class ValidationException extends Exception { @@ -22,7 +22,7 @@ public function __construct(object $object, array $failingRules) foreach ($failingRules as $field => $failingRulesForField) { /** @var Rule $failingRuleForField */ foreach ($failingRulesForField as $failingRuleForField) { - $messages[$field][] = StringHelper::naturalLangJoin(ArrayHelper::wrap($failingRuleForField->message())); + $messages[$field][] = LanguageHelper::join(ArrayHelper::wrap($failingRuleForField->message())); } } diff --git a/src/Validation/Rules/Password.php b/src/Validation/Rules/Password.php index 7264a82..1f6cc79 100644 --- a/src/Validation/Rules/Password.php +++ b/src/Validation/Rules/Password.php @@ -6,7 +6,7 @@ use Attribute; use Tempest\Validation\Rule; -use Tempest\Support\StringHelper; +use Tempest\Support\LanguageHelper; #[Attribute] final readonly class Password implements Rule From 2ddde79be0dca008637dbf684749b5500ec6c477 Mon Sep 17 00:00:00 2001 From: przemyslaw-przylucki Date: Thu, 30 May 2024 22:11:01 +0200 Subject: [PATCH 5/5] QA --- src/Support/LanguageHelper.php | 15 ++++++----- .../Exceptions/ValidationException.php | 2 +- src/Validation/Rules/Password.php | 4 ++- .../Validation/ValidationExceptionTest.php | 25 +++++++++---------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/Support/LanguageHelper.php b/src/Support/LanguageHelper.php index bfc7aa2..99fc1cf 100644 --- a/src/Support/LanguageHelper.php +++ b/src/Support/LanguageHelper.php @@ -6,16 +6,19 @@ final class LanguageHelper { - - public static function join(array $strings): string + /** + * @param string[] $parts + * + * @return string + */ + public static function join(array $parts): string { - $last = array_pop($strings); + $last = array_pop($parts); - if ($strings) { - return implode(', ', $strings) . ' ' . 'and' . ' ' . $last; + if ($parts) { + return implode(', ', $parts) . ' ' . 'and' . ' ' . $last; } return $last; } - } diff --git a/src/Validation/Exceptions/ValidationException.php b/src/Validation/Exceptions/ValidationException.php index 5917d57..3fa6b1d 100644 --- a/src/Validation/Exceptions/ValidationException.php +++ b/src/Validation/Exceptions/ValidationException.php @@ -5,9 +5,9 @@ namespace Tempest\Validation\Exceptions; use Exception; -use Tempest\Validation\Rule; use Tempest\Support\ArrayHelper; use Tempest\Support\LanguageHelper; +use Tempest\Validation\Rule; final class ValidationException extends Exception { diff --git a/src/Validation/Rules/Password.php b/src/Validation/Rules/Password.php index 1f6cc79..0aa9af5 100644 --- a/src/Validation/Rules/Password.php +++ b/src/Validation/Rules/Password.php @@ -6,7 +6,6 @@ use Attribute; use Tempest\Validation\Rule; -use Tempest\Support\LanguageHelper; #[Attribute] final readonly class Password implements Rule @@ -52,6 +51,9 @@ public function isValid(mixed $value): bool return true; } + /** + * @return string[] + */ public function message(): array { $messages = ["Value should contain at least {$this->min} characters"]; diff --git a/tests/Unit/Validation/ValidationExceptionTest.php b/tests/Unit/Validation/ValidationExceptionTest.php index 4333428..72e324e 100644 --- a/tests/Unit/Validation/ValidationExceptionTest.php +++ b/tests/Unit/Validation/ValidationExceptionTest.php @@ -4,14 +4,17 @@ namespace Tests\Tempest\Unit\Validation; -use stdClass; -use Tempest\Validation\Rule; use PHPUnit\Framework\TestCase; +use stdClass; use Tempest\Validation\Exceptions\ValidationException; +use Tempest\Validation\Rule; +/** + * @internal + * @small + */ final class ValidationExceptionTest extends TestCase { - public function test_exception_message(): void { $this->expectException(ValidationException::class); @@ -20,8 +23,7 @@ public function test_exception_message(): void throw new ValidationException(new stdClass(), [ 'email' => [ - new class implements Rule { - + new class () implements Rule { public function isValid(mixed $value): bool { return false; @@ -31,8 +33,8 @@ public function message(): string|array { return 'Value should be a valid email address'; } - } - ] + }, + ], ]); } @@ -45,8 +47,7 @@ public function test_exception_message_with_multiple_messages(): void throw new ValidationException(new stdClass(), [ 'email' => [ - new class implements Rule { - + new class () implements Rule { public function isValid(mixed $value): bool { return false; @@ -57,8 +58,7 @@ public function message(): string|array return 'Value should be a valid email address'; } }, - new class implements Rule { - + new class () implements Rule { public function isValid(mixed $value): bool { return false; @@ -72,8 +72,7 @@ public function message(): string|array 'the new gods from the future', ]; } - }] + }], ]); } - }