ConstraintViolationListNormalizer.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Serializer\Normalizer;
  11. use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
  12. use Symfony\Component\Validator\ConstraintViolationListInterface;
  13. /**
  14. * A normalizer that normalizes a ConstraintViolationListInterface instance.
  15. *
  16. * This Normalizer implements RFC7807 {@link https://tools.ietf.org/html/rfc7807}.
  17. *
  18. * @author Grégoire Pineau <lyrixx@lyrixx.info>
  19. * @author Kévin Dunglas <dunglas@gmail.com>
  20. */
  21. class ConstraintViolationListNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
  22. {
  23. public const INSTANCE = 'instance';
  24. public const STATUS = 'status';
  25. public const TITLE = 'title';
  26. public const TYPE = 'type';
  27. public const PAYLOAD_FIELDS = 'payload_fields';
  28. private $defaultContext;
  29. private $nameConverter;
  30. public function __construct($defaultContext = [], NameConverterInterface $nameConverter = null)
  31. {
  32. $this->defaultContext = $defaultContext;
  33. $this->nameConverter = $nameConverter;
  34. }
  35. /**
  36. * {@inheritdoc}
  37. *
  38. * @return array
  39. */
  40. public function normalize($object, string $format = null, array $context = [])
  41. {
  42. if (\array_key_exists(self::PAYLOAD_FIELDS, $context)) {
  43. $payloadFieldsToSerialize = $context[self::PAYLOAD_FIELDS];
  44. } elseif (\array_key_exists(self::PAYLOAD_FIELDS, $this->defaultContext)) {
  45. $payloadFieldsToSerialize = $this->defaultContext[self::PAYLOAD_FIELDS];
  46. } else {
  47. $payloadFieldsToSerialize = [];
  48. }
  49. if (\is_array($payloadFieldsToSerialize) && [] !== $payloadFieldsToSerialize) {
  50. $payloadFieldsToSerialize = array_flip($payloadFieldsToSerialize);
  51. }
  52. $violations = [];
  53. $messages = [];
  54. foreach ($object as $violation) {
  55. $propertyPath = $this->nameConverter ? $this->nameConverter->normalize($violation->getPropertyPath(), null, $format, $context) : $violation->getPropertyPath();
  56. $violationEntry = [
  57. 'propertyPath' => $propertyPath,
  58. 'title' => $violation->getMessage(),
  59. 'parameters' => $violation->getParameters(),
  60. ];
  61. if (null !== $code = $violation->getCode()) {
  62. $violationEntry['type'] = sprintf('urn:uuid:%s', $code);
  63. }
  64. $constraint = $violation->getConstraint();
  65. if (
  66. [] !== $payloadFieldsToSerialize &&
  67. $constraint &&
  68. $constraint->payload &&
  69. // If some or all payload fields are whitelisted, add them
  70. $payloadFields = null === $payloadFieldsToSerialize || true === $payloadFieldsToSerialize ? $constraint->payload : array_intersect_key($constraint->payload, $payloadFieldsToSerialize)
  71. ) {
  72. $violationEntry['payload'] = $payloadFields;
  73. }
  74. $violations[] = $violationEntry;
  75. $prefix = $propertyPath ? sprintf('%s: ', $propertyPath) : '';
  76. $messages[] = $prefix.$violation->getMessage();
  77. }
  78. $result = [
  79. 'type' => $context[self::TYPE] ?? $this->defaultContext[self::TYPE] ?? 'https://symfony.com/errors/validation',
  80. 'title' => $context[self::TITLE] ?? $this->defaultContext[self::TITLE] ?? 'Validation Failed',
  81. ];
  82. if (null !== $status = ($context[self::STATUS] ?? $this->defaultContext[self::STATUS] ?? null)) {
  83. $result['status'] = $status;
  84. }
  85. if ($messages) {
  86. $result['detail'] = implode("\n", $messages);
  87. }
  88. if (null !== $instance = ($context[self::INSTANCE] ?? $this->defaultContext[self::INSTANCE] ?? null)) {
  89. $result['instance'] = $instance;
  90. }
  91. return $result + ['violations' => $violations];
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public function supportsNormalization($data, string $format = null)
  97. {
  98. return $data instanceof ConstraintViolationListInterface;
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function hasCacheableSupportsMethod(): bool
  104. {
  105. return __CLASS__ === static::class;
  106. }
  107. }