Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
94.44% covered (success)
94.44%
17 / 18
85.00% covered (warning)
85.00%
17 / 20
21.74% covered (danger)
21.74%
5 / 23
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
UnprocessableEntityHttpExceptionNormalizer
94.44% covered (success)
94.44%
17 / 18
85.00% covered (warning)
85.00%
17 / 20
21.74% covered (danger)
21.74%
5 / 23
80.00% covered (warning)
80.00%
4 / 5
69.00
0.00% covered (danger)
0.00%
0 / 1
 supportsNormalization
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 normalizeErrors
92.86% covered (success)
92.86%
13 / 14
81.25% covered (warning)
81.25%
13 / 16
5.26% covered (danger)
5.26%
1 / 19
0.00% covered (danger)
0.00%
0 / 1
48.66
 getSupportedTypes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getErrorCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStatus
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Shared\Infrastructure\Normalizer;
4
5use App\Shared\Domain\Exception\ErrorCode;
6use InvalidArgumentException;
7use Symfony\Component\Clock\ClockAwareTrait;
8use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
9use Symfony\Component\HttpFoundation\Response;
10use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
11use Symfony\Component\Validator\Constraint;
12use Symfony\Component\Validator\Exception\ValidationFailedException;
13use Throwable;
14
15#[AutoconfigureTag('app.exception_normalizer')]
16class UnprocessableEntityHttpExceptionNormalizer extends ExceptionNormalizer
17{
18
19    use ClockAwareTrait;
20
21    /**
22     * @param mixed $data
23     * @param string|null $format
24     * @param array<string, mixed> $context
25     * @return bool
26     */
27    public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
28    {
29        return $data instanceof UnprocessableEntityHttpException;
30    }
31
32    /**
33     * @param Throwable $throwable
34     * @return array<string, array<string, string[]|int[]>>
35     */
36    protected function normalizeErrors(Throwable $throwable): array
37    {
38        if (!$throwable instanceof UnprocessableEntityHttpException) {
39            throw new InvalidArgumentException();
40        }
41
42        /**
43         * @var ValidationFailedException $validationFailedException
44         */
45        $validationFailedException = $throwable->getPrevious();
46
47        $errorsAsArray = [];
48        foreach ($validationFailedException->getViolations() as $violation) {
49            $propertyPath = $violation->getPropertyPath();
50            /** @var class-string<Constraint> $constraintClass */
51            $constraintClass = $violation->getConstraint() ? $violation->getConstraint()::class : null;
52            $detailCode = $constraintClass ? $constraintClass::getErrorName($violation->getCode()) : ErrorCode::UNKNOWN_ERROR->getCode();
53            if (!isset($errorsAsArray[$propertyPath])) {
54                $errorsAsArray[$propertyPath] = [];
55            }
56            if (!isset($errorsAsArray[$propertyPath][$detailCode])) {
57                $errorsAsArray[$propertyPath][$detailCode] = [];
58            }
59            $errorsAsArray[$propertyPath][$detailCode][strtolower($detailCode)] = $violation->getMessage();
60        }
61        return $errorsAsArray;
62    }
63
64    public function getSupportedTypes(?string $format): array
65    {
66        return [UnprocessableEntityHttpException::class => true];
67    }
68
69    #[\Override]
70    protected function getErrorCode(Throwable $throwable): string
71    {
72        return 'validation-error';
73    }
74
75    #[\Override]
76    protected function getStatus(Throwable $throwable): int
77    {
78        return Response::HTTP_UNPROCESSABLE_ENTITY;
79    }
80}