Completed
Push — master ( 5bd612...4d79db )
by Tarmo
19s queued 14s
created

EntityReferenceExistsValidator::validate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 2
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/App/Validator/Constraints/EntityReferenceExistsValidator.php
5
 *
6
 * @author TLe, Tarmo Leppänen <[email protected]>
7
 */
8
9
namespace App\Validator\Constraints;
10
11
use App\Entity\Interfaces\EntityInterface;
12
use Closure;
13
use Doctrine\ORM\EntityNotFoundException;
14
use Psr\Log\LoggerInterface;
15
use Symfony\Component\Validator\Constraint;
16
use Symfony\Component\Validator\ConstraintValidator;
17
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
18
use Symfony\Component\Validator\Exception\UnexpectedValueException;
19
use function array_filter;
20
use function array_map;
21
use function count;
22
use function implode;
23
use function is_array;
24
use function str_replace;
25
26
/**
27
 * Class EntityReferenceExistsValidator
28
 *
29
 * @package App\Validator\Constraints
30
 * @author TLe, Tarmo Leppänen <[email protected]>
31
 */
32
class EntityReferenceExistsValidator extends ConstraintValidator
33
{
34 8
    public function __construct(
35
        private readonly LoggerInterface $logger,
36
    ) {
37 8
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42 8
    public function validate(mixed $value, Constraint $constraint): void
43
    {
44 8
        if (!$constraint instanceof EntityReferenceExists) {
45 1
            throw new UnexpectedTypeException($constraint, EntityReferenceExists::class);
46
        }
47
48 7
        $values = $this->normalize($constraint->entityClass, $value);
49
50 2
        $this->check($values);
51
    }
52
53
    /**
54
     * Checks if the passed value is valid.
55
     *
56
     * @param EntityInterface|array<int, EntityInterface>|mixed $input
57
     *
58
     * @return array<int, EntityInterface>
59
     */
60 7
    private function normalize(string $target, mixed $input): array
61
    {
62 7
        $values = is_array($input) ? $input : [$input];
63
64 7
        foreach ($values as $value) {
65 7
            if (!$value instanceof $target) {
66 4
                throw new UnexpectedValueException($value, $target);
67
            }
68
69 3
            if (!$value instanceof EntityInterface) {
70 1
                throw new UnexpectedValueException($value, EntityInterface::class);
71
            }
72
        }
73
74 2
        return $values;
75
    }
76
77
    /**
78
     * @param array<int, EntityInterface> $entities
79
     */
80 2
    private function check(array $entities): void
81
    {
82 2
        $invalidIds = $this->getInvalidValues($entities);
83
84 2
        if ($invalidIds !== []) {
85 1
            $message = count($invalidIds) === 1
86 1
                ? EntityReferenceExists::MESSAGE_SINGLE
87
                : EntityReferenceExists::MESSAGE_MULTIPLE;
88 1
            $entity = $entities[0]::class;
89
90 1
            $parameterEntity = str_replace('Proxies\\__CG__\\', '', $entity);
91 1
            $parameterId = count($invalidIds) > 1 ? implode('", "', $invalidIds) : $invalidIds[0];
92
93 1
            $this->context
94 1
                ->buildViolation($message)
95 1
                ->setParameter('{{ entity }}', $parameterEntity)
96 1
                ->setParameter('{{ id }}', $parameterId)
97 1
                ->setCode(EntityReferenceExists::ENTITY_REFERENCE_EXISTS_ERROR)
98 1
                ->addViolation();
99
        }
100
    }
101
102
    /**
103
     * @param array<int, EntityInterface> $entities
104
     *
105
     * @return array<int, string>
106
     */
107 2
    private function getInvalidValues(array $entities): array
108
    {
109 2
        return array_map(
110 2
            static fn (EntityInterface $entity): string => $entity->getId(),
111 2
            array_filter($entities, $this->getFilterClosure())
112 2
        );
113
    }
114
115
    /**
116
     * Method to return used filter closure.
117
     */
118 2
    private function getFilterClosure(): Closure
119
    {
120 2
        return function (EntityInterface $entity): bool {
121 2
            $output = false;
122
123
            try {
124 2
                $entity->getCreatedAt();
125 1
            } catch (EntityNotFoundException $exception) {
126 1
                $this->logger->error($exception->getMessage());
127
128 1
                $output = true;
129
            }
130
131 2
            return $output;
132 2
        };
133
    }
134
}
135