[Serializer] Adds FormErrorNormalizer

This commit is contained in:
YaFou 2020-06-22 18:01:27 +02:00
parent 9f0eee1cb8
commit dcb8d8b05d
No known key found for this signature in database
GPG Key ID: A112DE339CED408A
6 changed files with 279 additions and 0 deletions

View File

@ -34,6 +34,7 @@ use Symfony\Component\Form\FormRegistry;
use Symfony\Component\Form\FormRegistryInterface; use Symfony\Component\Form\FormRegistryInterface;
use Symfony\Component\Form\ResolvedFormTypeFactory; use Symfony\Component\Form\ResolvedFormTypeFactory;
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface; use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;
use Symfony\Component\Form\Serializer\FormErrorNormalizer;
use Symfony\Component\Form\Util\ServerParams; use Symfony\Component\Form\Util\ServerParams;
return static function (ContainerConfigurator $container) { return static function (ContainerConfigurator $container) {
@ -140,5 +141,8 @@ return static function (ContainerConfigurator $container) {
param('validator.translation_domain'), param('validator.translation_domain'),
]) ])
->tag('form.type_extension') ->tag('form.type_extension')
->set('form.serializer.normalizer.form_error', FormErrorNormalizer::class)
->tag('serializer.normalizer', ['priority' => -915])
; ;
}; };

View File

@ -40,6 +40,7 @@ use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Serializer\FormErrorNormalizer;
use Symfony\Component\HttpClient\ScopingHttpClient; use Symfony\Component\HttpClient\ScopingHttpClient;
use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass;
use Symfony\Component\Messenger\Transport\TransportFactory; use Symfony\Component\Messenger\Transport\TransportFactory;
@ -1150,6 +1151,17 @@ abstract class FrameworkExtensionTest extends TestCase
$this->assertEquals(-910, $tag[0]['priority']); $this->assertEquals(-910, $tag[0]['priority']);
} }
public function testFormErrorNormalizerRegistred()
{
$container = $this->createContainerFromFile('full');
$definition = $container->getDefinition('form.serializer.normalizer.form_error');
$tag = $definition->getTag('serializer.normalizer');
$this->assertEquals(FormErrorNormalizer::class, $definition->getClass());
$this->assertEquals(-915, $tag[0]['priority']);
}
public function testJsonSerializableNormalizerRegistered() public function testJsonSerializableNormalizerRegistered()
{ {
$container = $this->createContainerFromFile('full'); $container = $this->createContainerFromFile('full');

View File

@ -1,6 +1,11 @@
CHANGELOG CHANGELOG
========= =========
5.2.0
-----
* added `FormErrorNormalizer`
5.1.0 5.1.0
----- -----

View File

@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Serializer;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Normalizes invalid Form instances.
*/
final class FormErrorNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
{
const TITLE = 'title';
const TYPE = 'type';
const CODE = 'status_code';
/**
* {@inheritdoc}
*/
public function normalize($object, $format = null, array $context = []): array
{
$data = [
'title' => $context[self::TITLE] ?? 'Validation Failed',
'type' => $context[self::TYPE] ?? 'https://symfony.com/errors/form',
'code' => $context[self::CODE] ?? null,
'errors' => $this->convertFormErrorsToArray($object),
];
if (0 !== \count($object->all())) {
$data['children'] = $this->convertFormChildrenToArray($object);
}
return $data;
}
/**
* {@inheritdoc}
*/
public function supportsNormalization($data, $format = null): bool
{
return $data instanceof FormInterface && $data->isSubmitted() && !$data->isValid();
}
private function convertFormErrorsToArray(FormInterface $data): array
{
$errors = [];
foreach ($data->getErrors() as $error) {
$errors[] = [
'message' => $error->getMessage(),
'cause' => $error->getCause(),
];
}
return $errors;
}
private function convertFormChildrenToArray(FormInterface $data): array
{
$children = [];
foreach ($data->all() as $child) {
$childData = [
'errors' => $this->convertFormErrorsToArray($child),
];
if (!empty($child->all())) {
$childData['children'] = $this->convertFormChildrenToArray($child);
}
$children[$child->getName()] = $childData;
}
return $children;
}
/**
* {@inheritdoc}
*/
public function hasCacheableSupportsMethod(): bool
{
return __CLASS__ === static::class;
}
}

View File

@ -0,0 +1,163 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Tests\Serializer;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormErrorIterator;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Serializer\FormErrorNormalizer;
class FormErrorNormalizerTest extends TestCase
{
/**
* @var FormErrorNormalizer
*/
private $normalizer;
/**
* @var FormInterface
*/
private $form;
protected function setUp(): void
{
$this->normalizer = new FormErrorNormalizer();
$this->form = $this->createMock(FormInterface::class);
$this->form->method('isSubmitted')->willReturn(true);
$this->form->method('all')->willReturn([]);
$this->form->method('getErrors')
->willReturn(new FormErrorIterator($this->form, [
new FormError('a', 'b', ['c', 'd'], 5, 'f'),
new FormError(1, 2, [3, 4], 5, 6),
])
);
}
public function testSupportsNormalizationWithWrongClass()
{
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
}
public function testSupportsNormalizationWithNotSubmittedForm()
{
$form = $this->createMock(FormInterface::class);
$this->assertFalse($this->normalizer->supportsNormalization($form));
}
public function testSupportsNormalizationWithValidForm()
{
$this->assertTrue($this->normalizer->supportsNormalization($this->form));
}
public function testNormalize()
{
$expected = [
'code' => null,
'title' => 'Validation Failed',
'type' => 'https://symfony.com/errors/form',
'errors' => [
[
'message' => 'a',
'cause' => 'f',
],
[
'message' => '1',
'cause' => 6,
],
],
];
$this->assertEquals($expected, $this->normalizer->normalize($this->form));
}
public function testNormalizeWithChildren()
{
$exptected = [
'code' => null,
'title' => 'Validation Failed',
'type' => 'https://symfony.com/errors/form',
'errors' => [
[
'message' => 'a',
'cause' => null,
],
],
'children' => [
'form1' => [
'errors' => [
[
'message' => 'b',
'cause' => null,
],
],
],
'form2' => [
'errors' => [
[
'message' => 'c',
'cause' => null,
],
],
'children' => [
'form3' => [
'errors' => [
[
'message' => 'd',
'cause' => null,
],
],
],
],
],
],
];
$form = clone $form1 = clone $form2 = clone $form3 = $this->createMock(FormInterface::class);
$form1->method('getErrors')
->willReturn(new FormErrorIterator($form1, [
new FormError('b'),
])
);
$form1->method('getName')->willReturn('form1');
$form2->method('getErrors')
->willReturn(new FormErrorIterator($form1, [
new FormError('c'),
])
);
$form2->method('getName')->willReturn('form2');
$form3->method('getErrors')
->willReturn(new FormErrorIterator($form1, [
new FormError('d'),
])
);
$form3->method('getName')->willReturn('form3');
$form2->method('all')->willReturn([$form3]);
$form = $this->createMock(FormInterface::class);
$form->method('isSubmitted')->willReturn(true);
$form->method('all')->willReturn([$form1, $form2]);
$form->method('getErrors')
->willReturn(new FormErrorIterator($form, [
new FormError('a'),
])
);
$this->assertEquals($exptected, $this->normalizer->normalize($form));
}
}

View File

@ -37,6 +37,7 @@
"symfony/http-foundation": "^4.4|^5.0", "symfony/http-foundation": "^4.4|^5.0",
"symfony/http-kernel": "^4.4|^5.0", "symfony/http-kernel": "^4.4|^5.0",
"symfony/security-csrf": "^4.4|^5.0", "symfony/security-csrf": "^4.4|^5.0",
"symfony/serializer": "^4.4|^5.0",
"symfony/translation": "^4.4|^5.0", "symfony/translation": "^4.4|^5.0",
"symfony/var-dumper": "^4.4|^5.0" "symfony/var-dumper": "^4.4|^5.0"
}, },