EntityValidator::validate()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7.0283

Importance

Changes 0
Metric Value
dl 0
loc 40
ccs 22
cts 24
cp 0.9167
rs 8.3466
c 0
b 0
f 0
cc 7
nc 7
nop 2
crap 7.0283
1
<?php
2
declare(strict_types=1);
3
4
namespace Paysera\Bundle\ApiBundle\Service\Validation;
5
6
use Paysera\Bundle\ApiBundle\Entity\ValidationOptions;
7
use Paysera\Bundle\ApiBundle\Entity\Violation;
8
use Paysera\Bundle\ApiBundle\Exception\ApiException;
9
use Symfony\Component\Validator\Validator\ValidatorInterface;
10
use Symfony\Component\Validator\Exception\InvalidArgumentException;
11
use Symfony\Component\Validator\ConstraintViolation;
12
use RuntimeException;
13
14
/**
15
 * Purpose:
16
 *     1. detect invalid properties
17
 *     2. throws exception with detailed information about each invalid property (name, message, violations).
18
 *
19
 * Use case:
20
 *     1. front-end sends data to REST API. If data invalid, then front-end will get detailed information
21
 *        about each invalid property (name, message).
22
 */
23
class EntityValidator
24
{
25
    protected $validator;
26
    protected $propertyPathConverter;
27
28 107
    public function __construct(
29
        ValidatorInterface $validator = null,
30
        PropertyPathConverterInterface $propertyPathConverter = null
31
    ) {
32 107
        $this->validator = $validator;
33 107
        $this->propertyPathConverter = $propertyPathConverter;
34 107
    }
35
36
    /**
37
     * Validates entity, throws InvalidDataException if some constraint fails.
38
     *
39
     * @param mixed $entity
40
     * @param ValidationOptions $options
41
     *
42
     * @throws ApiException
43
     */
44 60
    public function validate($entity, ValidationOptions $options)
45
    {
46 60
        if (!is_object($entity)) {
47 17
            return;
48
        }
49
50 43
        if ($this->validator === null) {
51
            throw new RuntimeException(
52
                'To use validation in RestBundle you must configure framework.validation in config.yml'
53
            );
54
        }
55
56 43
        $violationList = $this->validator->validate($entity, null, $options->getValidationGroups());
57
58 43
        if ($violationList->count() === 0) {
59 26
            return;
60
        }
61
62 19
        $violations = [];
63
64 19
        $violationPathMap = $options->getViolationPathMap();
65 19
        foreach ($violationList as $violation) {
66 19
            $path = $violation->getPropertyPath();
67 19
            if (isset($violationPathMap[$path])) {
68 9
                $path = $violationPathMap[$path];
69 11
            } elseif ($this->propertyPathConverter !== null) {
70 11
                $path = $this->propertyPathConverter->convert($path);
71
            }
72
73 19
            $violations[] = (new Violation())
74 19
                ->setField($path)
75 19
                ->setMessage($violation->getMessage())
76 19
                ->setCode($this->getErrorCode($violation))
77
            ;
78
        }
79
80 19
        $exception = new ApiException(ApiException::INVALID_PARAMETERS);
81 19
        $exception->setViolations($violations);
82 19
        throw $exception;
83
    }
84
85
    /**
86
     * @param ConstraintViolation $violation
87
     *
88
     * @return null|string
89
     */
90 19
    private function getErrorCode(ConstraintViolation $violation)
91
    {
92 19
        $constraint = $violation->getConstraint();
93
94 19
        if ($constraint === null || $violation->getCode() === null) {
95 5
            return null;
96
        }
97
98
        try {
99 14
            return mb_strtolower(
100 14
                str_replace('_ERROR', '', $constraint->getErrorName($violation->getCode()))
101
            );
102 1
        } catch (InvalidArgumentException $exception) {
103 1
            return $violation->getCode();
104
        }
105
    }
106
}
107