NotOverlappedDatesValidator::validateDatesOrder()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.6492

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 5
cts 11
cp 0.4545
rs 9.7
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2.6492
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the zibios/sharep.
7
 *
8
 * (c) Zbigniew Ślązak
9
 */
10
11
namespace App\Validator\Constraints;
12
13
use App\Entity\EntityInterface;
14
use App\Enum\Functional\ApplicationEnum;
15
use App\Repository\Functional\NotOverlappedDatesRepository;
16
use Symfony\Component\Validator\Constraint;
17
use Symfony\Component\Validator\ConstraintValidator;
18
use Symfony\Component\Validator\Exception\InvalidArgumentException;
19
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
20
use Symfony\Component\Validator\Exception\UnexpectedValueException;
21
22
class NotOverlappedDatesValidator extends ConstraintValidator
23
{
24
    /**
25
     * @var NotOverlappedDatesRepository
26
     */
27
    private $repository;
28
29 4
    public function __construct(NotOverlappedDatesRepository $repository)
30
    {
31 4
        $this->repository = $repository;
32 4
    }
33
34
    /**
35
     * @param EntityInterface               $entity
36
     * @param NotOverlappedDates|Constraint $constraint
37
     */
38 4
    public function validate($entity, Constraint $constraint): void
39
    {
40 4
        \assert($entity instanceof EntityInterface);
41 4
        \assert($constraint instanceof NotOverlappedDates);
42
43 4
        $this->assertInstances($entity, $constraint);
44 4
        $this->assertConstraintProperties($entity, $constraint);
45 4
        $this->assertDatesInstanceOf($entity, $constraint);
46
47 4
        if (false === $this->validateDatesNotEmpty($entity, $constraint)) {
48
            return;
49
        }
50
51 4
        if (false === $this->validateDatesOrder($entity, $constraint)) {
52
            return;
53
        }
54
55 4
        $overlappedEntities = $this->repository->getOverlappedEntities(
56 4
            \get_class($entity),
57 4
            $constraint->fromDateProperty,
58 4
            $constraint->toDateProperty,
59 4
            $this->getFromDate($entity, $constraint),
60 4
            $this->getToDate($entity, $constraint)
61
        );
62
63 4
        if (1 === \count($overlappedEntities) && $overlappedEntities[0]->getId() === $entity->getId()) {
64 1
            return;
65
        }
66
67 4
        if (\count($overlappedEntities) > 0) {
68
            $periodsString = '';
69
            foreach ($overlappedEntities as $overlappedEntity) {
70
                $periodsString .= sprintf(
71
                    '%s - %s,',
72
                    $this->getFromDateString($overlappedEntity, $constraint),
73
                    $this->getToDateString($overlappedEntity, $constraint)
74
                );
75
            }
76
            $this->context->buildViolation($constraint::INVALID_PERIOD_OVERLAPPED)
77
                ->setParameter('{{ fromDate }}', $this->getFromDateString($entity, $constraint))
78
                ->setParameter('{{ toDate }}', $this->getToDateString($entity, $constraint))
79
                ->setParameter('{{ periods }}', $periodsString)
80
                ->atPath($constraint->toDateProperty)
81
                ->addViolation();
82
        }
83 4
    }
84
85
    //------------------------------------------------------------------------------------------------------------------
86
87 4
    private function assertInstances($entity, Constraint $constraint): void
88
    {
89 4
        \assert($entity instanceof EntityInterface);
90 4
        \assert($constraint instanceof NotOverlappedDates);
91
92 4
        if (!$constraint instanceof NotOverlappedDates) {
93
            throw new UnexpectedTypeException($constraint, NotOverlappedDates::class);
94
        }
95 4
        if (!$entity instanceof EntityInterface) {
96
            throw new UnexpectedValueException($entity, EntityInterface::class);
97
        }
98 4
    }
99
100 4
    private function assertConstraintProperties(EntityInterface $entity, NotOverlappedDates $constraint): void
101
    {
102 4
        if (!method_exists($entity, $constraint->fromDateMethod)) {
103
            throw new InvalidArgumentException(
104
                sprintf('Method \'%s\' not exist \'%s\'', $constraint->fromDateMethod, \get_class($entity))
105
            );
106
        }
107 4
        if (!\is_callable([$entity, $constraint->fromDateMethod])) {
108
            throw new InvalidArgumentException(
109
                sprintf('Method \'%s\' not public \'%s\'', $constraint->fromDateMethod, \get_class($entity))
110
            );
111
        }
112 4
        if (!property_exists($entity, $constraint->fromDateProperty)) {
113
            throw new InvalidArgumentException(
114
                sprintf('Property \'%s\' not exist \'%s\'', $constraint->fromDateProperty, \get_class($entity))
115
            );
116
        }
117
118 4
        if (!method_exists($entity, $constraint->toDateMethod)) {
119
            throw new InvalidArgumentException(
120
                sprintf('Method \'%s\' not exist in \'%s\'', $constraint->toDateMethod, \get_class($entity))
121
            );
122
        }
123 4
        if (!\is_callable([$entity, $constraint->toDateMethod])) {
124
            throw new InvalidArgumentException(
125
                sprintf('Method \'%s\' not public \'%s\'', $constraint->toDateMethod, \get_class($entity))
126
            );
127
        }
128 4
        if (!property_exists($entity, $constraint->toDateProperty)) {
129
            throw new InvalidArgumentException(
130
                sprintf('Property \'%s\' not exist \'%s\'', $constraint->toDateProperty, \get_class($entity))
131
            );
132
        }
133 4
    }
134
135 4
    private function assertDatesInstanceOf(EntityInterface $entity, NotOverlappedDates $constraint): void
136
    {
137 4
        $fromDate = $entity->{$constraint->fromDateMethod}();
138 4
        if (null !== $fromDate && !$fromDate instanceof \DateTimeInterface) {
139
            throw new UnexpectedValueException($fromDate, \DateTimeInterface::class);
140
        }
141 4
        $toDate = $entity->{$constraint->toDateMethod}();
142 4
        if (null !== $toDate && !$toDate instanceof \DateTimeInterface) {
143
            throw new UnexpectedValueException($toDate, \DateTimeInterface::class);
144
        }
145 4
    }
146
147 4
    private function validateDatesNotEmpty(EntityInterface $entity, NotOverlappedDates $constraint): bool
0 ignored issues
show
Coding Style introduced by
function validateDatesNotEmpty() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
148
    {
149 4
        $result = true;
150 4
        if (!$this->getFromDate($entity, $constraint) instanceof \DateTimeInterface) {
151
            $this->context->buildViolation($constraint::INVALID_FROM_DATE)
152
                ->setParameter('{{ fromDate }}', 'N/A')
153
                ->atPath($constraint->fromDateProperty)
154
                ->addViolation();
155
            $result = false;
156
        }
157 4
        if (!$this->getToDate($entity, $constraint) instanceof \DateTimeInterface) {
158
            $this->context->buildViolation($constraint::INVALID_TO_DATE)
159
                ->setParameter('{{ toDate }}', 'N/A')
160
                ->atPath($constraint->toDateProperty)
161
                ->addViolation();
162
            $result = false;
163
        }
164
165 4
        return $result;
166
    }
167
168 4
    private function validateDatesOrder(EntityInterface $entity, NotOverlappedDates $constraint): bool
0 ignored issues
show
Coding Style introduced by
function validateDatesOrder() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
169
    {
170 4
        $fromDateString = $this->getFromDateString($entity, $constraint);
171 4
        $toDateString = $this->getToDateString($entity, $constraint);
172
173 4
        if ($fromDateString >= $toDateString) {
174
            $this->context->buildViolation($constraint::INVALID_ORDER)
175
                ->setParameter('{{ fromDate }}', $fromDateString)
176
                ->setParameter('{{ toDate }}', $toDateString)
177
                ->atPath($constraint->toDateProperty)
178
                ->addViolation();
179
180
            return false;
181
        }
182
183 4
        return true;
184
    }
185
186 4
    private function getFromDate(EntityInterface $entity, NotOverlappedDates $constraint): \DateTimeInterface
187
    {
188 4
        return $entity->{$constraint->fromDateMethod}();
189
    }
190
191 4
    private function getToDate(EntityInterface $entity, NotOverlappedDates $constraint): \DateTimeInterface
192
    {
193 4
        return $entity->{$constraint->toDateMethod}();
194
    }
195
196 4
    private function getFromDateString(EntityInterface $entity, NotOverlappedDates $constraint): string
197
    {
198 4
        return $this->getFromDate($entity, $constraint)->format(ApplicationEnum::DATE_FORMAT);
199
    }
200
201 4
    private function getToDateString(EntityInterface $entity, NotOverlappedDates $constraint): string
202
    {
203 4
        return $this->getToDate($entity, $constraint)->format(ApplicationEnum::DATE_FORMAT);
204
    }
205
}
206