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