[Serializer] Adds FormErrorNormalizer
This commit is contained in:
parent
9f0eee1cb8
commit
dcb8d8b05d
@ -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])
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
@ -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');
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
5.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added `FormErrorNormalizer`
|
||||||
|
|
||||||
5.1.0
|
5.1.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user