From 78032f2daeebe559d5666b57c7f820dcb7006634 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Wed, 10 Apr 2019 23:37:37 +0200 Subject: [PATCH] Validator::formatMessage: support dynamic error messages This is useful when you want to show user an informative error message, whose text depends on some external data that depends on the value of some other field. For example, my form allows user to choose a category of a team. Each category has a different limit for number of allowed members, which is specified in config.neon. I want to show the user the limit as part of the error message when she manages to exceed the limit. --- src/Forms/Helpers.php | 3 +++ src/Forms/Rules.php | 2 +- src/Forms/Validator.php | 9 +++++++++ tests/Forms/Helpers.exportRules.phpt | 11 +++++++++++ tests/Forms/Rules.formatMessage.phpt | 28 +++++++++++++++++++++++++++- 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Forms/Helpers.php b/src/Forms/Helpers.php index 55d701076..6d4702b67 100644 --- a/src/Forms/Helpers.php +++ b/src/Forms/Helpers.php @@ -102,6 +102,9 @@ public static function exportRules(Rules $rules): array } $op = Nette\Utils\Callback::toString($op); } + if (is_callable($rule->message)) { + continue; + } if ($rule->branch) { $item = [ 'op' => ($rule->isNegative ? '~' : '') . $op, diff --git a/src/Forms/Rules.php b/src/Forms/Rules.php index 6c2c8c9da..6c1b40311 100644 --- a/src/Forms/Rules.php +++ b/src/Forms/Rules.php @@ -69,7 +69,7 @@ public function isRequired(): bool /** * Adds a validation rule for the current control. * @param callable|string $validator - * @param string|object $errorMessage + * @param string|callable|object $errorMessage * @return static */ public function addRule($validator, $errorMessage = null, $arg = null) diff --git a/src/Forms/Validator.php b/src/Forms/Validator.php index cb618a4fe..6ae8a7e88 100644 --- a/src/Forms/Validator.php +++ b/src/Forms/Validator.php @@ -51,6 +51,15 @@ class Validator public static function formatMessage(Rule $rule, bool $withValue = true) { $message = $rule->message; + if (is_callable($message)) { + $params = Nette\Utils\Callback::toReflection($message)->getParameters(); + if (isset($params[1])) { + $message = $message($rule->control, $rule->arg); + } else { + $message = $message($rule->control); + } + } + if ($message instanceof Nette\Utils\IHtmlString) { return $message; diff --git a/tests/Forms/Helpers.exportRules.phpt b/tests/Forms/Helpers.exportRules.phpt index 5fc76976d..670ae121a 100644 --- a/tests/Forms/Helpers.exportRules.phpt +++ b/tests/Forms/Helpers.exportRules.phpt @@ -6,6 +6,7 @@ declare(strict_types=1); +use Nette\Forms\Controls\TextInput; use Nette\Forms\Form; use Nette\Forms\Helpers; use Tester\Assert; @@ -91,3 +92,13 @@ test(function () { ], ], Helpers::exportRules($input2->getRules())); }); + + +test(function () { + $form = new Form; + $input = $form->addText('text'); + $input->addRule(Form::EMAIL, function (TextInput $input, $arg) { + return $input->getValue() . ' is not valid e-mail address.'; + }); + Assert::same([], Helpers::exportRules($input->getRules())); +}); diff --git a/tests/Forms/Rules.formatMessage.phpt b/tests/Forms/Rules.formatMessage.phpt index 942d19202..c40fbed59 100644 --- a/tests/Forms/Rules.formatMessage.phpt +++ b/tests/Forms/Rules.formatMessage.phpt @@ -6,6 +6,7 @@ declare(strict_types=1); +use Nette\Forms\Controls\TextInput; use Nette\Forms\Form; use Tester\Assert; @@ -34,11 +35,36 @@ $form->addText('special', 'Label:') ->addRule(Form::EMAIL, '%label %value is invalid [field %name] %d', $form['special']) ->setDefaultValue('xyz'); +$form->addText('function', 'Label:') + ->setRequired() + ->addRule(function (TextInput $input, string $arg) { + return strpos($input->getValue(), $arg) === false; + }, function (TextInput $input, string $arg) { + return "String “{$input->getValue()}” contains a letter “{$arg}”, which is not allowed"; + }, 'a') + ->setDefaultValue('banana'); + +$form->addText('functionWithoutArg', 'Label:') + ->setRequired() + ->addRule(function (TextInput $input) { + return strpos($input->getValue(), 'e') === false; + }, function (TextInput $input) { + return "String “{$input->getValue()}” contains a letter “e”, which is not allowed"; + }) + ->setDefaultValue('orange'); + $form->validate(); Assert::true($form->hasErrors()); -Assert::same(['1 5', '5 1', '1 ', 'Label xyz is invalid [field special] xyz'], $form->getErrors()); +Assert::same([ + '1 5', + '5 1', + '1 ', + 'Label xyz is invalid [field special] xyz', + 'String “banana” contains a letter “a”, which is not allowed', + 'String “orange” contains a letter “e”, which is not allowed', +], $form->getErrors()); Assert::same([], $form->getOwnErrors());