Failed Conditions
Push — master ( 8f957f...3e56ea )
by Zbigniew
05:35
created

NotOverlappedDatesValidator::validate()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 11.1032

Importance

Changes 0
Metric Value
dl 0
loc 46
ccs 18
cts 32
cp 0.5625
rs 8.2448
c 0
b 0
f 0
cc 7
nc 6
nop 2
crap 11.1032
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 View Code Duplication
        if (!\is_callable([$entity, $constraint->fromDateMethod])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
108
            throw new InvalidArgumentException(
109
                sprintf('Method \'%s\' not public \'%s\'', $constraint->fromDateMethod, \get_class($entity))
110
            );
111
        }
112 4 View Code Duplication
        if (!property_exists($entity, $constraint->fromDateProperty)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
113
            throw new InvalidArgumentException(
114
                sprintf('Property \'%s\' not exist \'%s\'', $constraint->fromDateProperty, \get_class($entity))
115
            );
116
        }
117
118 4 View Code Duplication
        if (!method_exists($entity, $constraint->toDateMethod)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
119
            throw new InvalidArgumentException(
120
                sprintf('Method \'%s\' not exist in \'%s\'', $constraint->toDateMethod, \get_class($entity))
121
            );
122
        }
123 4 View Code Duplication
        if (!\is_callable([$entity, $constraint->toDateMethod])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
            throw new InvalidArgumentException(
125
                sprintf('Method \'%s\' not public \'%s\'', $constraint->toDateMethod, \get_class($entity))
126
            );
127
        }
128 4 View Code Duplication
        if (!property_exists($entity, $constraint->toDateProperty)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
        if (!$this->getFromDate($entity, $constraint) instanceof \DateTimeInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
        if (!$this->getToDate($entity, $constraint) instanceof \DateTimeInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
        if ($fromDateString >= $toDateString) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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