From 2ae1df177a53fc8c13514e6dea4df8da01670c02 Mon Sep 17 00:00:00 2001 From: Henrique Moody Date: Tue, 26 Mar 2024 12:53:15 +0100 Subject: [PATCH] Allow to customise messages while asserting Because we now have a single "assert()" method, we have more freedom to add more customizations to it. This specific one is handy if someone wants to use the library to validate but wants to use their own exceptions. Signed-off-by: Henrique Moody --- docs/02-feature-guide.md | 14 ++-- library/Validator.php | 47 +++++++++++- .../get_messages_with_replacements.phpt | 38 +++++----- tests/integration/lib/helpers.php | 14 +--- tests/integration/readme/custom_messages.phpt | 5 +- tests/unit/ValidatorTest.php | 75 +++++++++++++++++++ 6 files changed, 149 insertions(+), 44 deletions(-) diff --git a/docs/02-feature-guide.md b/docs/02-feature-guide.md index 4c6184d3..646fbcee 100644 --- a/docs/02-feature-guide.md +++ b/docs/02-feature-guide.md @@ -199,15 +199,13 @@ method as well by passing the templates as an argument: ```php try { - $usernameValidator->assert('really messed up screen#name'); + $usernameValidator->assert('really messed up screen#name', [ + 'alnum' => '{{name}} must contain only letters and digits', + 'noWhitespace' => '{{name}} cannot contain spaces', + 'length' => '{{name}} must not have more than 15 chars', + ]); } catch(NestedValidationException $exception) { - print_r( - $exception->getMessages([ - 'alnum' => '{{name}} must contain only letters and digits', - 'noWhitespace' => '{{name}} cannot contain spaces', - 'length' => '{{name}} must not have more than 15 chars', - ]) - ); + print_r($exception->getMessages()); } ``` diff --git a/library/Validator.php b/library/Validator.php index f96fb8ae..9b33bfb8 100644 --- a/library/Validator.php +++ b/library/Validator.php @@ -16,15 +16,17 @@ use Respect\Validation\Message\StandardFormatter; use Respect\Validation\Message\StandardRenderer; use Respect\Validation\Mixins\StaticValidator; use Respect\Validation\Rules\AllOf; -use Respect\Validation\Rules\Core\Standard; +use Throwable; use function count; use function current; +use function is_array; +use function is_string; /** * @mixin StaticValidator */ -final class Validator extends Standard +final class Validator implements Validatable { use CanBindEvaluateRule; @@ -34,6 +36,10 @@ final class Validator extends Standard /** @var array */ private array $templates = []; + private ?string $name = null; + + private ?string $template = null; + public function __construct( private readonly Factory $factory, private readonly Formatter $formatter, @@ -66,15 +72,24 @@ final class Validator extends Standard return $this->evaluate($input)->isValid; } - public function assert(mixed $input): void + /** @param array|string|Throwable|null $template */ + public function assert(mixed $input, array|string|Throwable|null $template = null): void { $result = $this->evaluate($input); if ($result->isValid) { return; } + if ($template instanceof Throwable) { + throw $template; + } + $templates = $this->templates; - if (count($templates) === 0 && $this->getTemplate() != null) { + if (is_array($template)) { + $templates = $template; + } elseif (is_string($template)) { + $templates = ['__root__' => $template]; + } elseif ($this->getTemplate() != null) { $templates = ['__root__' => $this->getTemplate()]; } @@ -115,6 +130,30 @@ final class Validator extends Standard $this->assert($input); } + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getTemplate(): ?string + { + return $this->template; + } + + public function setTemplate(string $template): static + { + $this->template = $template; + + return $this; + } + private function rule(): Validatable { if (count($this->rules) === 1) { diff --git a/tests/integration/get_messages_with_replacements.phpt b/tests/integration/get_messages_with_replacements.phpt index 8352c6e9..cfd19849 100644 --- a/tests/integration/get_messages_with_replacements.phpt +++ b/tests/integration/get_messages_with_replacements.phpt @@ -26,25 +26,27 @@ exceptionMessages( ->key('password', v::stringType()) ->key('schema', v::stringType()) ) - ->setTemplates([ - 'mysql' => [ - 'user' => 'Value should be a MySQL username', - 'host' => '`{{name}}` should be a MySQL host', + ->assert( + [ + 'mysql' => [ + 'host' => 42, + 'schema' => 42, + ], + 'postgresql' => [ + 'user' => 42, + 'password' => 42, + ], ], - 'postgresql' => [ - 'schema' => 'You must provide a valid PostgreSQL schema', - ], - ]) - ->assert([ - 'mysql' => [ - 'host' => 42, - 'schema' => 42, - ], - 'postgresql' => [ - 'user' => 42, - 'password' => 42, - ], - ]); + [ + 'mysql' => [ + 'user' => 'Value should be a MySQL username', + 'host' => '`{{name}}` should be a MySQL host', + ], + 'postgresql' => [ + 'schema' => 'You must provide a valid PostgreSQL schema', + ], + ] + ); } ); ?> diff --git a/tests/integration/lib/helpers.php b/tests/integration/lib/helpers.php index 624ca57e..2b993b44 100644 --- a/tests/integration/lib/helpers.php +++ b/tests/integration/lib/helpers.php @@ -51,19 +51,11 @@ function run(array $scenarios): void echo $description . PHP_EOL; echo str_repeat('⎺', strlen($description)) . PHP_EOL; - if (is_string($template)) { - $rule->setTemplate($template); - } - - if (is_array($template)) { - $rule->setTemplates($template); - } - $fallbackMessage = 'No exception was thrown with: ' . stringify($input); - exceptionMessage(static fn() => $rule->check($input), $fallbackMessage); - exceptionFullMessage(static fn() => $rule->assert($input), $fallbackMessage); - exceptionMessages(static fn() => $rule->assert($input), $fallbackMessage); + exceptionMessage(static fn() => $rule->assert($input, $template), $fallbackMessage); + exceptionFullMessage(static fn() => $rule->assert($input, $template), $fallbackMessage); + exceptionMessages(static fn() => $rule->assert($input, $template), $fallbackMessage); echo PHP_EOL; } } diff --git a/tests/integration/readme/custom_messages.phpt b/tests/integration/readme/custom_messages.phpt index 3b6f77f9..32f77b06 100644 --- a/tests/integration/readme/custom_messages.phpt +++ b/tests/integration/readme/custom_messages.phpt @@ -11,12 +11,11 @@ exceptionMessages( static fn() => v::alnum() ->noWhitespace() ->length(v::between(1, 15)) - ->setTemplates([ + ->assert('really messed up screen#name', [ 'alnum' => '{{name}} must contain only letters and digits', 'noWhitespace' => '{{name}} cannot contain spaces', 'length' => '{{name}} must not have more than 15 chars', ]) - ->assert('really messed up screen#name') ); ?> --EXPECT-- @@ -25,4 +24,4 @@ exceptionMessages( 'alnum' => '"really messed up screen#name" must contain only letters and digits', 'noWhitespace' => '"really messed up screen#name" cannot contain spaces', 'lengthBetween' => 'The length of "really messed up screen#name" must be between 1 and 15', -] \ No newline at end of file +] diff --git a/tests/unit/ValidatorTest.php b/tests/unit/ValidatorTest.php index fdab91d1..7ba76f56 100644 --- a/tests/unit/ValidatorTest.php +++ b/tests/unit/ValidatorTest.php @@ -9,7 +9,9 @@ declare(strict_types=1); namespace Respect\Validation; +use Exception; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use Respect\Validation\Exceptions\ComponentException; use Respect\Validation\Test\Rules\Stub; @@ -49,4 +51,77 @@ final class ValidatorTest extends TestCase self::assertFalse($validator->isValid('whatever')); } + + #[Test] + public function itShouldAssertUsingTheGivingExceptionEvenWhenRuleAlreadyHasTemplate(): void + { + $template = new Exception('This is a test'); + + $this->expectExceptionObject($template); + + $validator = Validator::create(Stub::fail(1)); + $validator->setTemplate('This wont be used'); + $validator->assert('whatever', $template); + } + + #[Test] + public function itShouldAssertUsingTheGivingStringTemplate(): void + { + $template = 'This is my new template'; + + $this->expectExceptionMessage($template); + + $validator = Validator::create(Stub::fail(1)); + $validator->assert('whatever', $template); + } + + #[Test] + public function itShouldAssertUsingTheGivingArrayTemplateWithTheRuleNameAsKey(): void + { + $template = ['stub' => 'This is my new template']; + + $this->expectExceptionMessage($template['stub']); + + $validator = Validator::create(Stub::fail(1)); + $validator->setTemplates(['stub' => 'This is my pre-defined template']); + $validator->assert('whatever', $template); + } + + #[Test] + public function itShouldAssertUsingTheGivingArrayTemplateWithRootKey(): void + { + $template = ['__root__' => 'This is my new template']; + + $this->expectExceptionMessage($template['__root__']); + + $validator = Validator::create(Stub::fail(1)); + $validator->setTemplates(['__root__' => 'This is my pre-defined template']); + $validator->assert('whatever', $template); + } + + /** @param string|array $template */ + #[Test] + #[DataProvider('providerForTemplates')] + public function itShouldAssertNotOverwritingThePreDefinedTemplate(array|string $template): void + { + $preDefinedTemplate = 'This is my pre-defined template'; + + $this->expectExceptionMessage($preDefinedTemplate); + + $validator = Validator::create(Stub::fail(1)); + $validator->setTemplate($preDefinedTemplate); + $validator->assert('whatever', $template); + } + + /** + * @return array}> + */ + public static function providerForTemplates(): array + { + return [ + 'string' => ['This is my new template'], + 'array key named key' => [['stub' => 'This is my new template']], + 'array key __root__ key' => [['__root__' => 'This is my new template']], + ]; + } }