diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index b36b179775671..d6eac5c79d609 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -73,6 +73,7 @@ use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; +use Symfony\Component\Serializer\Normalizer\ConstraintViolationListNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Stopwatch\Stopwatch; @@ -1227,6 +1228,10 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.normalizer.dateinterval'); } + if (!class_exists(ConstraintViolationListNormalizer::class)) { + $container->removeDefinition('serializer.normalizer.constraint_violation_list'); + } + if (!class_exists(ClassDiscriminatorFromClassMetadata::class)) { $container->removeAlias('Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface'); $container->removeDefinition('serializer.mapping.class_discriminator_resolver'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml index 90c4d1c5b5050..54b0c484d20bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml @@ -31,6 +31,11 @@ + + + + + diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index d45e771e610b8..9c1dccb5d9fe9 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG maximum depth is reached * added optional `int[] $ignoredNodeTypes` argument to `XmlEncoder::__construct`. XML decoding now ignores comment node types by default. +* added `ConstraintViolationListNormalizer` 4.0.0 ----- @@ -33,7 +34,7 @@ CHANGELOG * added support for serializing `DateInterval` objects * added getter for extra attributes in `ExtraAttributesException` * improved `CsvEncoder` to handle variable nested structures - * CSV headers can be passed to the `CsvEncoder` via the `csv_headers` serialization context variable + * CSV headers can be passed to the `CsvEncoder` via the `csv_headers` serialization context variable * added `$context` when checking for encoding, decoding and normalizing in `Serializer` 3.3.0 diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php new file mode 100644 index 0000000000000..68a4cb9213279 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Normalizer; + +use Symfony\Component\Validator\ConstraintViolationListInterface; + +/** + * A normalizer that normalizes a ConstraintViolationListInterface instance. + * + * This Normalizer implements RFC7807 {@link https://tools.ietf.org/html/rfc7807}. + * + * + * @author Grégoire Pineau + * @author Kévin Dunglas + */ +class ConstraintViolationListNormalizer implements NormalizerInterface +{ + /** + * {@inheritdoc} + */ + public function normalize($object, $format = null, array $context = array()) + { + $violations = array(); + $messages = array(); + foreach ($object as $violation) { + $violations[] = array( + 'propertyPath' => $violation->getPropertyPath(), + 'message' => $violation->getMessage(), + 'code' => $violation->getCode(), + ); + $propertyPath = $violation->getPropertyPath(); + $prefix = $propertyPath ? sprintf('%s: ', $propertyPath) : ''; + $messages[] = $prefix.$violation->getMessage(); + } + + return array( + 'title' => isset($context['title']) ? $context['title'] : 'An error occurred', + 'detail' => $messages ? implode("\n", $messages) : '', + 'violations' => $violations, + ); + } + + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof ConstraintViolationListInterface; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php new file mode 100644 index 0000000000000..5c9c55028ff2f --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ConstraintViolationListNormalizerTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Normalizer\ConstraintViolationListNormalizer; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationList; + +/** + * @author Grégoire Pineau + * @author Kévin Dunglas + */ +class ConstraintViolationListNormalizerTest extends TestCase +{ + private $normalizer; + + protected function setUp() + { + $this->normalizer = new ConstraintViolationListNormalizer(); + } + + public function testSupportsNormalization() + { + $this->assertTrue($this->normalizer->supportsNormalization(new ConstraintViolationList())); + $this->assertFalse($this->normalizer->supportsNormalization(new \stdClass())); + } + + public function testNormalize() + { + $list = new ConstraintViolationList(array( + new ConstraintViolation('a', 'b', array(), 'c', 'd', 'e', null, 'f'), + new ConstraintViolation('1', '2', array(), '3', '4', '5', null, '6'), + )); + + $expected = array( + 'title' => 'An error occurred', + 'detail' => 'd: a +4: 1', + 'violations' => array( + array( + 'propertyPath' => 'd', + 'message' => 'a', + 'code' => 'f', + ), + array( + 'propertyPath' => '4', + 'message' => '1', + 'code' => '6', + ), + ), + ); + + $this->assertEquals($expected, $this->normalizer->normalize($list)); + } +} diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 4de54606d928d..c950a39c3cb28 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -25,6 +25,7 @@ "symfony/http-foundation": "~3.4|~4.0", "symfony/cache": "~3.4|~4.0", "symfony/property-info": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", "doctrine/annotations": "~1.0", "symfony/dependency-injection": "~3.4|~4.0", "doctrine/cache": "~1.0",