Make getMessages() return the names or ids as keys

This method change the behavior of "getMessages()" by changin the keys
of its return to the "id" of the specific exception that was triggered.

It also allows users to overwrite the templates by passing an array to
it.

This is being requested by many users for a long time. It took that long
because I was thinking too much about how to improve the old method
called "findMesssage()" that I didn't realize that it could be done in a
easier way with the "getMessages()".

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
Henrique Moody 2018-03-07 19:07:52 +01:00
parent 8c529c433e
commit 71ecea32e6
No known key found for this signature in database
GPG key ID: 221E9281655813A6
9 changed files with 276 additions and 44 deletions

View file

@ -167,10 +167,8 @@ The printed message is exactly this, as a nested Markdown list:
## Getting all messages as an array
The Markdown list is fine, but unusable on a HTML form or something more custom.
For that you can use `getMessages()`.
It will return all messages from the rules that did not pass the validation.
If you want to get all the messages as an array you can use `getMessages()` for
that. The `getMessages()` method returns an array with all the messages.
```php
try {
@ -180,17 +178,56 @@ try {
}
```
The code above may display something like:
The `getMessages()` returns an array in which the keys are the name of the
validators, or its reference in case you are using [Key](Key.md) or
[Attribute](Attribute.md) rule:
```no-highlight
Array
(
[0] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
[1] => "really messed up screen#name" must not contain whitespace
[2] => "really messed up screen#name" must have a length between 1 and 15
[alnum] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
[noWhitespace] => "really messed up screen#name" must not contain whitespace
[length] => "really messed up screen#name" must have a length between 1 and 15
)
```
## Custom messages
Getting messages as an array is fine, but sometimes you need to customize them
in order to present them to the user. This is possible using the `getMessages()`
method as well by passing the templates as an argument:
```php
try {
$usernameValidator->assert('really messed up screen#name');
} 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',
])
);
}
```
For all messages, the `{{name}}` variable is available for templates. If you do
not define a name it uses the input to replace this placeholder.
The result of the code above will be:
```no-highlight
Array
(
[alnum] => "really messed up screen#name" must contain only letters and digits
[noWhitespace] => "really messed up screen#name" cannot contain spaces
[length] => "really messed up screen#name" must not have more than 15 chars
)
```
Note that `getMessage()` will only return a message when the specific validation
in the chain fails.
## Message localization
You're also able to translate your message to another language with Validation.

View file

@ -16,6 +16,8 @@ namespace Respect\Validation\Exceptions;
use IteratorAggregate;
use RecursiveIteratorIterator;
use SplObjectStorage;
use function count;
use function is_array;
class NestedValidationException extends ValidationException implements IteratorAggregate
{
@ -119,18 +121,29 @@ class NestedValidationException extends ValidationException implements IteratorA
return $childrenExceptions;
}
/**
* @return array
*/
public function getMessages()
public function getMessages(array $templates = []): array
{
$messages = [$this->getMessage()];
foreach ($this as $exception) {
$messages[] = $exception->getMessage();
$messages = [$this->getId() => $this->renderMessage($this, $templates)];
foreach ($this->getRelated() as $exception) {
$id = $exception->getId();
if (!$exception instanceof self) {
$messages[$id] = $this->renderMessage(
$exception,
$this->findTemplates($templates, $this->getId())
);
continue;
}
$messages[$id] = $exception->getMessages($this->findTemplates($templates, $id, $this->getId()));
if (count($messages[$id]) > 1) {
continue;
}
$messages[$id] = current($messages[$exception->getId()]);
}
if (count($messages) > 1) {
array_shift($messages);
unset($messages[$this->getId()]);
}
return $messages;
@ -159,7 +172,7 @@ class NestedValidationException extends ValidationException implements IteratorA
}
/**
* @return SplObjectStorage
* @return SplObjectStorage|ValidationException[]
*/
public function getRelated()
{
@ -170,25 +183,6 @@ class NestedValidationException extends ValidationException implements IteratorA
return $this->exceptions;
}
/**
* @param string $name
* @param mixed $value
*
* @return self
*/
public function setParam($name, $value)
{
if ('translator' === $name) {
foreach ($this->getRelated() as $exception) {
$exception->setParam($name, $value);
}
}
parent::setParam($name, $value);
return $this;
}
/**
* @param array $exceptions
*
@ -202,4 +196,25 @@ class NestedValidationException extends ValidationException implements IteratorA
return $this;
}
private function renderMessage(ValidationException $exception, array $templates): string
{
if (isset($templates[$exception->getId()])) {
$exception->updateTemplate($templates[$exception->getId()]);
}
return $exception->getMessage();
}
private function findTemplates(array $templates, ...$ids): array
{
while (count($ids) > 0) {
$id = array_shift($ids);
if (isset($templates[$id]) && is_array($templates[$id])) {
$templates = $templates[$id];
}
}
return $templates;
}
}

View file

@ -0,0 +1,62 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
try {
v::create()
->key(
'mysql',
v::create()
->key('host', v::stringType(), true)
->key('user', v::stringType(), true)
->key('password', v::stringType(), true)
->key('schema', v::stringType(), true),
true
)
->key(
'postgresql',
v::create()
->key('host', v::stringType(), true)
->key('user', v::stringType(), true)
->key('password', v::stringType(), true)
->key('schema', v::stringType(), true),
true
)
->assert([
'mysql' => [
'host' => 42,
'schema' => 42,
],
'postgresql' => [
'user' => 42,
'password' => 42,
],
]);
} catch (NestedValidationException $exception) {
print_r($exception->getMessages());
}
?>
--EXPECTF--
Array
(
[mysql] => Array
(
[host] => host must be of type string
[user] => Key user must be present
[password] => Key password must be present
[schema] => schema must be of type string
)
[postgresql] => Array
(
[host] => Key host must be present
[user] => user must be of type string
[password] => password must be of type string
[schema] => Key schema must be present
)
)

View file

@ -29,8 +29,8 @@ try {
--EXPECTF--
Array
(
[0] => username must have a length between 2 and 32
[1] => birthdate must be a valid date/time
[2] => password must not be empty
[3] => Key email must be present
[username] => username must have a length between 2 and 32
[birthdate] => birthdate must be a valid date/time
[password] => password must not be empty
[email] => Key email must be present
)

View file

@ -0,0 +1,70 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
try {
v::create()
->key(
'mysql',
v::create()
->key('host', v::stringType(), true)
->key('user', v::stringType(), true)
->key('password', v::stringType(), true)
->key('schema', v::stringType(), true),
true
)
->key(
'postgresql',
v::create()
->key('host', v::stringType(), true)
->key('user', v::stringType(), true)
->key('password', v::stringType(), true)
->key('schema', v::stringType(), true),
true
)
->assert([
'mysql' => [
'host' => 42,
'schema' => 42,
],
'postgresql' => [
'user' => 42,
'password' => 42,
],
]);
} catch (NestedValidationException $exception) {
print_r($exception->getMessages([
'mysql' => [
'user' => 'Value should be a MySQL username',
'host' => '{{input}} should be a MySQL host',
],
'postgresql' => [
'schema' => 'You must provide a valid PostgreSQL schema',
],
]));
}
?>
--EXPECTF--
Array
(
[mysql] => Array
(
[host] => 42 should be a MySQL host
[user] => Value should be a MySQL username
[password] => Key password must be present
[schema] => schema must be of type string
)
[postgresql] => Array
(
[host] => Key host must be present
[user] => user must be of type string
[password] => password must be of type string
[schema] => You must provide a valid PostgreSQL schema
)
)

View file

@ -23,5 +23,5 @@ try {
--EXPECTF--
Array
(
[0] => name must have a length between 2 and 32
[name] => name must have a length between 2 and 32
)

View file

@ -0,0 +1,27 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
$usernameValidator = v::alnum()->noWhitespace()->length(1, 15);
try {
$usernameValidator->assert('really messed up screen#name');
} 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',
])
);
}
?>
--EXPECTF--
Array
(
[alnum] => "really messed up screen#name" must contain only letters and digits
[noWhitespace] => "really messed up screen#name" cannot contain spaces
[length] => "really messed up screen#name" must not have more than 15 chars
)

View file

@ -15,7 +15,7 @@ try {
--EXPECTF--
Array
(
[0] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
[1] => "really messed up screen#name" must not contain whitespace
[2] => "really messed up screen#name" must have a length between 1 and 15
[alnum] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
[noWhitespace] => "really messed up screen#name" must not contain whitespace
[length] => "really messed up screen#name" must have a length between 1 and 15
)

View file

@ -0,0 +1,21 @@
--FILE--
<?php
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator as v;
$usernameValidator = v::alnum()->noWhitespace()->length(1, 15);
try {
$usernameValidator->assert('really messed up screen#name');
} catch (NestedValidationException $exception) {
print_r($exception->getMessages());
}
?>
--EXPECTF--
Array
(
[alnum] => "really messed up screen#name" must contain only letters (a-z) and digits (0-9)
[noWhitespace] => "really messed up screen#name" must not contain whitespace
[length] => "really messed up screen#name" must have a length between 1 and 15
)