Passed
Push — main ( 0f5def...1304a4 )
by Fractal
02:54
created

RequestConverter   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 47
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 22
c 1
b 0
f 0
dl 0
loc 47
ccs 21
cts 21
cp 1
rs 10
wmc 9

3 Methods

Rating   Name   Duplication   Size   Complexity  
A convert() 0 24 6
A __construct() 0 8 1
A validate() 0 4 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FRZB\Component\RequestMapper\Converter;
6
7
use FRZB\Component\DependencyInjection\Attribute\AsService;
8
use FRZB\Component\RequestMapper\Data\Context;
9
use FRZB\Component\RequestMapper\Data\ValidationError;
10
use FRZB\Component\RequestMapper\Exception\ClassExtractorException;
11
use FRZB\Component\RequestMapper\Exception\ConstraintException;
12
use FRZB\Component\RequestMapper\Exception\ConverterException;
13
use FRZB\Component\RequestMapper\Exception\ValidationException;
14
use FRZB\Component\RequestMapper\Extractor\ConstraintExtractor;
15
use FRZB\Component\RequestMapper\Extractor\DiscriminatorMapExtractor;
16
use FRZB\Component\RequestMapper\Extractor\ParametersExtractor;
17
use FRZB\Component\RequestMapper\Parser\ExceptionConverterInterface as ExceptionConverter;
18
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
19
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface as Denormalizer;
20
use Symfony\Component\Validator\Constraints\Collection;
21
use Symfony\Component\Validator\Validator\ValidatorInterface as Validator;
22
23
#[AsService]
24
class RequestConverter implements ConverterInterface
25
{
26
    private const DENORMALIZE_TYPE = 'array';
27
28 20
    public function __construct(
29
        private readonly Validator $validator,
30
        private readonly Denormalizer $denormalizer,
31
        private readonly ExceptionConverter $exceptionConverter,
32
        private readonly DiscriminatorMapExtractor $classExtractor,
33
        private readonly ConstraintExtractor $constraintExtractor,
34
        private readonly ParametersExtractor $parametersExtractor,
35
    ) {
36
    }
37
38 20
    public function convert(Context $context): object
39
    {
40 20
        $parameters = $context->getRequestParameters();
41 20
        $parameterClass = $context->getParameterClass() ?? throw ConverterException::nullableParameterClass();
42
43
        try {
44 19
            $targetClass = $this->classExtractor->extract($parameterClass, $parameters);
45
46 17
            if ($context->isValidationNeeded()) {
47 17
                $parameters = $this->parametersExtractor->extract($parameterClass, $parameters);
48 17
                $constraints = $this->constraintExtractor->extract($targetClass, $parameters);
49
50 17
                $this->validate($parameters, $context->getValidationGroups(), $constraints);
51
            }
52
53 15
            return $this->denormalizer->denormalize($parameters, $targetClass, self::DENORMALIZE_TYPE, $context->getSerializerContext());
54 7
        } catch (ClassExtractorException $e) {
55 1
            throw ValidationException::fromErrors(ValidationError::fromTypeAndClassExtractorException(DiscriminatorMap::class, $e));
56 6
        } catch (ConstraintException $e) {
57 2
            throw ValidationException::fromConstraintViolationList($e->getViolations());
58 4
        } catch (\TypeError $e) {
59 3
            throw ValidationException::fromErrors($this->exceptionConverter->convert($e, $parameters));
60 1
        } catch (\Throwable $e) {
61 1
            throw ConverterException::fromThrowable($e);
62
        }
63
    }
64
65
    /** @throws ValidationException */
66 17
    private function validate(mixed $target, array $validationGroups, ?Collection $constraints = null): void
67
    {
68 17
        if (($violations = $this->validator->validate($target, $constraints, $validationGroups))->count()) {
69 2
            throw ConstraintException::fromConstraintViolationList($violations);
70
        }
71
    }
72
}
73