Improve message when nested "Not" rules fail

When using nested "Not" and "AllOf" rules Validation does not fetch the
message of the rule that failed, and instead it fetches the default
message in "AllOfException."

That is because "Not" cannot reach the rules inside "AllOf," making it
impossible to fetch the correct message.

This commit will improve that a bit but making "Not" deal directly with
the rule inside "AllOf" when it has only one rule.

Unfortunately, it will not fix all the issues. For example, when the
negated rule is an "AllOf" with multiple rules and it fails "Not" will
fetch only the first message.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
Henrique Moody 2019-04-05 22:44:42 +02:00
parent 344b00cc07
commit 1b844763a9
No known key found for this signature in database
GPG key ID: 221E9281655813A6
3 changed files with 53 additions and 21 deletions

View file

@ -16,6 +16,8 @@ namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use function array_shift;
use function count;
use function current;
/**
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
@ -31,7 +33,12 @@ final class Not extends AbstractRule
public function __construct(Validatable $rule)
{
$this->rule = $rule;
$this->rule = $this->extractNegatedRule($rule);
}
public function getNegatedRule(): Validatable
{
return $this->rule;
}
public function setName(string $name): Validatable
@ -90,4 +97,22 @@ final class Not extends AbstractRule
return $rule;
}
private function extractNegatedRule(Validatable $rule): Validatable
{
if ($rule instanceof self && $rule->getNegatedRule() instanceof self) {
return $this->extractNegatedRule($rule->getNegatedRule()->getNegatedRule());
}
if (!$rule instanceof AllOf) {
return $rule;
}
$rules = $rule->getRules();
if (count($rules) === 1) {
return $this->extractNegatedRule(current($rules));
}
return $rule;
}
}

View file

@ -1,33 +1,40 @@
--CREDITS--
Henrique Moody <henriquemoody@gmail.com>
--TEST--
not() with recursion should update mode of its children
--FILE--
<?php
declare(strict_types=1);
declare(strict_types=1);
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Validator;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validator as v;
try {
$validator = Validator::not(
Validator::not(
Validator::not(
Validator::not(
Validator::not(
Validator::intVal()->positive()
)
$validator = v::not(
v::not(
v::not(
v::not(
v::not(
v::intVal()->positive()
)
)
)
);
)
);
try {
$validator->check(2);
} catch (ValidationException $exception) {
echo $exception->getMessage().PHP_EOL;
}
try {
$validator->assert(2);
} catch (NestedValidationException $exception) {
echo $exception->getFullMessage().PHP_EOL;
}
?>
--EXPECT--
- These rules must not pass for 2
2 must not be positive
- 2 must not be positive

View file

@ -69,7 +69,7 @@ final class NotTest extends TestCase
$not->setName('Foo');
self::assertEquals('Foo', $not->getName());
self::assertEquals('Foo', $rule->getName());
self::assertEquals('Foo', $not->getNegatedRule()->getName());
}
/**
@ -108,11 +108,11 @@ final class NotTest extends TestCase
public function providerForSetName(): array
{
return [
[new IntVal()],
[new AllOf(new NumericVal(), new IntVal())],
[new Not(new Not(new IntVal()))],
[Validator::intVal()->setName('Bar')],
[Validator::noneOf(Validator::numericVal(), Validator::intVal())],
'non-allOf' => [new IntVal()],
'allOf' => [new AllOf(new NumericVal(), new IntVal())],
'not' => [new Not(new Not(new IntVal()))],
'allOf with name' => [Validator::intVal()->setName('Bar')],
'noneOf' => [Validator::noneOf(Validator::numericVal(), Validator::intVal())],
];
}
}