mirror of
https://github.com/Respect/Validation.git
synced 2024-06-09 01:02:16 +02:00
Move message formatting out of ValidationException
There should not be too much code in the ValidationException. It is hard to test and debug code in exceptions because PHP does not trace further than their constructor. This commit will move the message formatting from ValidationException into a Formatter class (inside a Message namespace). Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
parent
ab602ae1bb
commit
00f61b9bdc
|
@ -14,11 +14,8 @@ declare(strict_types=1);
|
|||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function call_user_func;
|
||||
use function is_string;
|
||||
use Respect\Validation\Message\Formatter;
|
||||
use function key;
|
||||
use function preg_replace_callback;
|
||||
use function Respect\Stringifier\stringify;
|
||||
|
||||
/**
|
||||
* Default exception class for rule validations.
|
||||
|
@ -67,9 +64,9 @@ class ValidationException extends InvalidArgumentException implements Exception
|
|||
private $params = [];
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
* @var Formatter
|
||||
*/
|
||||
private $translator;
|
||||
private $formatter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -80,12 +77,12 @@ class ValidationException extends InvalidArgumentException implements Exception
|
|||
* @param mixed $input
|
||||
* @param mixed[] $params
|
||||
*/
|
||||
public function __construct($input, string $id, array $params, callable $translator)
|
||||
public function __construct($input, string $id, array $params, Formatter $formatter)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->id = $id;
|
||||
$this->params = $params;
|
||||
$this->translator = $translator;
|
||||
$this->formatter = $formatter;
|
||||
$this->template = $this->chooseTemplate();
|
||||
|
||||
parent::__construct($this->createMessage());
|
||||
|
@ -150,43 +147,10 @@ class ValidationException extends InvalidArgumentException implements Exception
|
|||
|
||||
private function createMessage(): string
|
||||
{
|
||||
$template = $this->createTemplate($this->mode, $this->template);
|
||||
$params = $this->getParams();
|
||||
$params['name'] = $params['name'] ?? stringify($this->input);
|
||||
$params['input'] = $this->input;
|
||||
|
||||
return $this->format($template, $params);
|
||||
}
|
||||
|
||||
private function createTemplate(string $mode, string $template): string
|
||||
{
|
||||
if (isset($this->defaultTemplates[$mode][$template])) {
|
||||
$template = $this->defaultTemplates[$mode][$template];
|
||||
}
|
||||
|
||||
return call_user_func($this->translator, $template);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $vars
|
||||
*/
|
||||
private function format(string $template, array $vars = []): string
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/{{(\w+)}}/',
|
||||
static function ($match) use ($vars) {
|
||||
if (!isset($vars[$match[1]])) {
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
$value = $vars[$match[1]];
|
||||
if ($match[1] == 'name' && is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return stringify($value);
|
||||
},
|
||||
$template
|
||||
return $this->formatter->format(
|
||||
$this->defaultTemplates[$this->mode][$this->template] ?? $this->template,
|
||||
$this->input,
|
||||
$this->params
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use ReflectionObject;
|
|||
use Respect\Validation\Exceptions\ComponentException;
|
||||
use Respect\Validation\Exceptions\InvalidClassException;
|
||||
use Respect\Validation\Exceptions\ValidationException;
|
||||
use Respect\Validation\Message\Formatter;
|
||||
use function lcfirst;
|
||||
use function sprintf;
|
||||
use function trim;
|
||||
|
@ -148,7 +149,7 @@ final class Factory
|
|||
}
|
||||
}
|
||||
|
||||
return new ValidationException($input, $id, $params, $this->translator);
|
||||
return new ValidationException($input, $id, $params, new Formatter($this->translator));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +189,7 @@ final class Factory
|
|||
): ValidationException {
|
||||
/** @var ValidationException $exception */
|
||||
$exception = $this->createReflectionClass($exceptionName, ValidationException::class)
|
||||
->newInstance($input, $id, $params, $this->translator);
|
||||
->newInstance($input, $id, $params, new Formatter($this->translator));
|
||||
if (isset($params['template'])) {
|
||||
$exception->updateTemplate($params['template']);
|
||||
}
|
||||
|
|
58
library/Message/Formatter.php
Normal file
58
library/Message/Formatter.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Respect/Validation.
|
||||
*
|
||||
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the "LICENSE.md"
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Respect\Validation\Message;
|
||||
|
||||
use function call_user_func;
|
||||
use function is_string;
|
||||
use function preg_replace_callback;
|
||||
use function Respect\Stringifier\stringify;
|
||||
|
||||
final class Formatter
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
public function __construct(callable $translator)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $input
|
||||
* @param mixed[] $parameters
|
||||
*/
|
||||
public function format(string $template, $input, array $parameters): string
|
||||
{
|
||||
$parameters['name'] = $parameters['name'] ?? stringify($input);
|
||||
|
||||
return preg_replace_callback(
|
||||
'/{{(\w+)}}/',
|
||||
static function ($match) use ($parameters) {
|
||||
if (!isset($parameters[$match[1]])) {
|
||||
return $match[0];
|
||||
}
|
||||
|
||||
$value = $parameters[$match[1]];
|
||||
if ($match[1] == 'name' && is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return stringify($value);
|
||||
},
|
||||
call_user_func($this->translator, $template)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace Respect\Validation\Test;
|
||||
|
||||
use Respect\Validation\Exceptions\ValidationException;
|
||||
use Respect\Validation\Message\Formatter;
|
||||
use Respect\Validation\Validatable;
|
||||
use function realpath;
|
||||
use function sprintf;
|
||||
|
@ -99,7 +100,7 @@ abstract class RuleTestCase extends TestCase
|
|||
'validatable',
|
||||
'input',
|
||||
[],
|
||||
'trim'
|
||||
new Formatter('strval')
|
||||
);
|
||||
$checkException->updateTemplate(sprintf('Exception for %s:check() method', $mockClassName));
|
||||
$validatableMocked
|
||||
|
@ -110,7 +111,7 @@ abstract class RuleTestCase extends TestCase
|
|||
'validatable',
|
||||
'input',
|
||||
[],
|
||||
'trim'
|
||||
new Formatter('strval')
|
||||
);
|
||||
$assertException->updateTemplate(sprintf('Exception for %s:assert() method', $mockClassName));
|
||||
$validatableMocked
|
||||
|
|
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
use Respect\Validation\Message\Formatter;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
|
||||
/**
|
||||
|
@ -29,8 +30,8 @@ final class NestedValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function getChildrenShouldReturnExceptionAddedByAddRelated(): void
|
||||
{
|
||||
$composite = new AttributeException('input', 'id', [], 'trim');
|
||||
$node = new IntValException('input', 'id', [], 'trim');
|
||||
$composite = new AttributeException('input', 'id', [], new Formatter('strval'));
|
||||
$node = new IntValException('input', 'id', [], new Formatter('strval'));
|
||||
$composite->addChild($node);
|
||||
self::assertCount(1, $composite->getChildren());
|
||||
self::assertContainsOnly(IntValException::class, $composite->getChildren());
|
||||
|
@ -41,8 +42,8 @@ final class NestedValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function addingTheSameInstanceShouldAddJustOneSingleReference(): void
|
||||
{
|
||||
$composite = new AttributeException('input', 'id', [], 'trim');
|
||||
$node = new IntValException('input', 'id', [], 'trim');
|
||||
$composite = new AttributeException('input', 'id', [], new Formatter('strval'));
|
||||
$node = new IntValException('input', 'id', [], new Formatter('strval'));
|
||||
$composite->addChild($node);
|
||||
$composite->addChild($node);
|
||||
$composite->addChild($node);
|
||||
|
|
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Respect\Validation\Exceptions;
|
||||
|
||||
use Respect\Validation\Message\Formatter;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
use function trim;
|
||||
|
||||
|
@ -32,7 +33,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldImplementException(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
|
||||
self::assertInstanceOf(Exception::class, $sut);
|
||||
}
|
||||
|
@ -43,7 +44,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
public function itShouldRetrieveId(): void
|
||||
{
|
||||
$id = 'my id';
|
||||
$sut = new ValidationException('input', $id, [], 'trim');
|
||||
$sut = new ValidationException('input', $id, [], new Formatter('strval'));
|
||||
|
||||
self::assertSame($id, $sut->getId());
|
||||
}
|
||||
|
@ -55,7 +56,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
{
|
||||
$params = ['foo' => true, 'bar' => 23];
|
||||
|
||||
$sut = new ValidationException('input', 'id', $params, 'trim');
|
||||
$sut = new ValidationException('input', 'id', $params, new Formatter('strval'));
|
||||
|
||||
self::assertSame($params, $sut->getParams());
|
||||
}
|
||||
|
@ -68,7 +69,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
$name = 'any name';
|
||||
$value = 'any value';
|
||||
|
||||
$sut = new ValidationException('input', 'id', [$name => $value], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [$name => $value], new Formatter('strval'));
|
||||
|
||||
self::assertSame($value, $sut->getParam($name));
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldReturnNullWhenParameterCanNotBeFound(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
|
||||
self::assertNull($sut->getParam('foo'));
|
||||
}
|
||||
|
@ -88,7 +89,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldHaveTemplateByDefault(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
|
||||
self::assertSame('"input" must be valid', $sut->getMessage());
|
||||
}
|
||||
|
@ -98,7 +99,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldUpdateMode(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
$sut->updateMode(ValidationException::MODE_NEGATIVE);
|
||||
|
||||
self::assertSame('"input" must not be valid', $sut->getMessage());
|
||||
|
@ -111,7 +112,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
{
|
||||
$template = 'This is my new template';
|
||||
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
$sut->updateTemplate($template);
|
||||
|
||||
self::assertEquals($template, $sut->getMessage());
|
||||
|
@ -122,7 +123,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldTellWhenHasAsCustomUpdateTemplate(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
|
||||
self::assertFalse($sut->hasCustomTemplate());
|
||||
|
||||
|
@ -134,12 +135,12 @@ final class ValidationExceptionTest extends TestCase
|
|||
/**
|
||||
* @test
|
||||
*/
|
||||
public function itShouldUseTranslator(): void
|
||||
public function itShouldUseFormatter(): void
|
||||
{
|
||||
$template = ' This is my new template ';
|
||||
$expected = trim($template);
|
||||
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('trim'));
|
||||
$sut->updateTemplate($template);
|
||||
|
||||
self::assertEquals($expected, $sut->getMessage());
|
||||
|
@ -150,7 +151,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldReplacePlaceholders(): void
|
||||
{
|
||||
$sut = new ValidationException('foo', 'id', ['bar' => 1, 'baz' => 2], 'trim');
|
||||
$sut = new ValidationException('foo', 'id', ['bar' => 1, 'baz' => 2], new Formatter('strval'));
|
||||
$sut->updateTemplate('{{name}} {{bar}} {{baz}}');
|
||||
|
||||
self::assertEquals(
|
||||
|
@ -164,7 +165,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldKeepPlaceholdersThatCanNotReplace(): void
|
||||
{
|
||||
$sut = new ValidationException('foo', 'id', ['foo' => 1], 'trim');
|
||||
$sut = new ValidationException('foo', 'id', ['foo' => 1], new Formatter('strval'));
|
||||
$sut->updateTemplate('{{name}} {{foo}} {{bar}}');
|
||||
|
||||
self::assertEquals(
|
||||
|
@ -178,7 +179,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldUpdateParams(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', ['foo' => 1], 'trim');
|
||||
$sut = new ValidationException('input', 'id', ['foo' => 1], new Formatter('strval'));
|
||||
$sut->updateTemplate('{{foo}}');
|
||||
$sut->updateParams(['foo' => 2]);
|
||||
|
||||
|
@ -190,7 +191,7 @@ final class ValidationExceptionTest extends TestCase
|
|||
*/
|
||||
public function itShouldConvertToString(): void
|
||||
{
|
||||
$sut = new ValidationException('input', 'id', [], 'trim');
|
||||
$sut = new ValidationException('input', 'id', [], new Formatter('strval'));
|
||||
|
||||
self::assertSame('"input" must be valid', (string) $sut);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace Respect\Validation\Rules;
|
||||
|
||||
use Respect\Validation\Exceptions\ValidationException;
|
||||
use Respect\Validation\Message\Formatter;
|
||||
use Respect\Validation\Test\TestCase;
|
||||
|
||||
/**
|
||||
|
@ -118,7 +119,9 @@ final class AbstractRuleTest extends TestCase
|
|||
->expects(self::once())
|
||||
->method('reportError')
|
||||
->with($input)
|
||||
->will(self::throwException(new ValidationException($input, 'abstract', [], 'trim')));
|
||||
->will(self::throwException(
|
||||
new ValidationException($input, 'abstract', [], new Formatter('strval'))
|
||||
));
|
||||
|
||||
$abstractRuleMock->assert($input);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue