ReservationCheckerEventSubscriber::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
c 0
b 0
f 0
nc 1
nop 9
dl 0
loc 6
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace App\Notification\EventSubscriber;
4
5
use App\Entity\Exam;
6
use App\Entity\ResourceReservation;
7
use App\Entity\Substitution;
8
use App\Entity\Teacher;
9
use App\Entity\Tuition;
10
use App\Event\SubstitutionImportEvent;
11
use App\Notification\Notification;
12
use App\Notification\NotificationService;
13
use App\Repository\ExamRepositoryInterface;
14
use App\Repository\ResourceReservationRepositoryInterface;
15
use App\Repository\UserRepositoryInterface;
16
use App\Rooms\Reservation\ResourceAvailabilityHelper;
17
use App\Validator\NoReservationCollision;
18
use DateTime;
19
use SchulIT\CommonBundle\Helper\DateHelper;
20
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
21
use Symfony\Component\Mailer\MailerInterface;
22
use Symfony\Component\Mime\Address;
23
use Symfony\Component\Mime\Email;
24
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
25
use Symfony\Component\Validator\ConstraintViolation;
26
use Symfony\Component\Validator\ConstraintViolationInterface;
27
use Symfony\Component\Validator\ConstraintViolationListInterface;
28
use Symfony\Component\Validator\Validator\ValidatorInterface;
29
use Symfony\Contracts\Translation\TranslatorInterface;
30
use Twig\Environment;
31
32
class ReservationCheckerEventSubscriber implements EventSubscriberInterface {
33
34
    public function __construct(private readonly ValidatorInterface $validator, private readonly ResourceReservationRepositoryInterface $reservationRepository,
35
                                private readonly ExamRepositoryInterface $examRepository, private readonly TranslatorInterface $translator,
36
                                private readonly DateHelper $dateHelper, private readonly ResourceAvailabilityHelper $availabilityHelper,
37
                                private readonly UserRepositoryInterface $userRepository, private readonly NotificationService $notificationService,
38
                                private readonly UrlGeneratorInterface $urlGenerator)
39
    {
40
    }
41
42
    public function onSubstitutionImportEvent(SubstitutionImportEvent $event): void {
43
        $substitutions = array_merge($event->getAdded(), $event->getUpdated());
44
        /** @var DateTime $start */
45
        $start = null;
46
47
        /** @var DateTime $end */
48
        $end = null;
49
50
        /** @var Substitution $substitution */
51
        foreach($substitutions as $substitution) {
52
            if($start === null || $substitution->getDate() < $start) {
53
                $start = clone $substitution->getDate();
54
            }
55
56
            if($end === null || $substitution->getDate() > $end) {
57
                $end = clone $substitution->getDate();
58
            }
59
        }
60
61
        if($start === null || $end === null) {
62
            return;
63
        }
64
65
        $today = $this->dateHelper->getToday();
66
67
        while($start <= $end) {
68
            $reservations = $this->reservationRepository->findAllByDate($start);
69
70
            foreach($reservations as $reservation) {
71
                if($reservation->getDate() >= $today) {
72
                    $violations = $this->validator->validate($reservation);
73
74
                    if (count($violations) > 0 && $this->handleViolation($reservation) === false) {
75
                        $this->sendViolationsEmail($reservation, $violations);
76
                    }
77
                }
78
            }
79
80
            $exams = $this->examRepository->findAllByDate($start);
81
82
            foreach($exams as $exam) {
83
                if(!empty($exam->getExternalId())) {
84
                    continue; // Disable check for external exams
85
                }
86
87
                $violations = $this->validator->validate($exam);
88
89
90
                $reservationViolations = [ ];
91
92
                foreach($violations as $violation) {
93
                    if($violation instanceof ConstraintViolation && $violation->getConstraint() instanceof NoReservationCollision) {
94
                        $reservationViolations[] = $violation;
95
                    }
96
                }
97
98
                if(count($reservationViolations) > 0) {
99
                    $this->sendExamViolationsEmail($exam, $reservationViolations);
100
                }
101
            }
102
103
            $start->modify('+1 day');
104
        }
105
    }
106
107
    private function handleViolation(ResourceReservation $reservation): bool {
108
        $conflictingSubstitutions = 0;
109
110
        for($lesson = $reservation->getLessonStart(); $lesson <= $reservation->getLessonEnd(); $lesson++) {
111
            $availability = $this->availabilityHelper->getAvailability($reservation->getResource(), $reservation->getDate(), $lesson);
0 ignored issues
show
Bug introduced by
It seems like $reservation->getDate() can also be of type null; however, parameter $date of App\Rooms\Reservation\Re...lper::getAvailability() does only seem to accept DateTime, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
            $availability = $this->availabilityHelper->getAvailability($reservation->getResource(), /** @scrutinizer ignore-type */ $reservation->getDate(), $lesson);
Loading history...
Bug introduced by
It seems like $reservation->getResource() can also be of type null; however, parameter $resource of App\Rooms\Reservation\Re...lper::getAvailability() does only seem to accept App\Entity\ResourceEntity, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
            $availability = $this->availabilityHelper->getAvailability(/** @scrutinizer ignore-type */ $reservation->getResource(), $reservation->getDate(), $lesson);
Loading history...
112
113
            if($availability === null) {
114
                continue;
115
            }
116
117
            if(($availability->getTimetableLesson() !== null && !$availability->isTimetableLessonCancelled()) || count($availability->getExams()) > 0) {
118
                return false;
119
            }
120
121
            $substitution = $availability->getSubstitution();
122
            if($substitution !== null) {
123
                $teachers = $substitution->getTeachers()->map(fn(Teacher $teacher) => $teacher->getId())->toArray();
124
                $replacementTeachers = $substitution->getReplacementTeachers()->map(fn(Teacher $teacher) => $teacher->getId())->toArray();
125
126
                if (!(count($replacementTeachers) > 0 && in_array($reservation->getTeacher()->getId(), $replacementTeachers) || count($replacementTeachers) === 0 && in_array($reservation->getTeacher()->getId(), $teachers))) {
127
                    $conflictingSubstitutions++;
128
                }
129
            }
130
        }
131
132
        if($conflictingSubstitutions === 0) {
0 ignored issues
show
introduced by
The condition $conflictingSubstitutions === 0 is always true.
Loading history...
133
            $this->sendReservationRemovedEmail($reservation);
134
            $this->reservationRepository->remove($reservation);
135
136
            return true;
137
        }
138
139
        return false;
140
    }
141
142
    private function sendReservationRemovedEmail(ResourceReservation $reservation): void {
143
        if($reservation->getTeacher() === null) {
144
            return;
145
        }
146
147
        foreach($this->userRepository->findAllTeachers([$reservation->getTeacher()]) as $recipient) {
148
            $notification = new Notification(
149
                $recipient,
150
                $this->translator->trans('reservation_removed.title', [], 'email'),
151
                $this->translator->trans('reservation_removed.content', [
152
                    '%resource%' => $reservation->getResource()->getName(),
153
                    '%date%' => $reservation->getDate()->format($this->translator->trans('date.format_short')),
154
                    '%lesson%' => $this->translator->trans('label.substitution_lessons', [
155
                        '%start%' => $reservation->getLessonStart(),
156
                        '%end%' => $reservation->getLessonEnd(),
157
                        '%count%' => ($reservation->getLessonEnd() - $reservation->getLessonStart())
158
                    ])
159
                ], 'email'),
160
                $this->urlGenerator->generate('substitutions', [], UrlGeneratorInterface::ABSOLUTE_URL),
161
                $this->translator->trans('reservation_removed.link', [], 'email')
162
            );
163
164
            $this->notificationService->notify($notification);
165
        }
166
    }
167
168
    private function sendViolationsEmail(ResourceReservation $reservation, ConstraintViolationListInterface $violationList): void {
0 ignored issues
show
Unused Code introduced by
The parameter $violationList is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

168
    private function sendViolationsEmail(ResourceReservation $reservation, /** @scrutinizer ignore-unused */ ConstraintViolationListInterface $violationList): void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
169
        if($reservation->getTeacher() === null) {
170
            return;
171
        }
172
173
        foreach($this->userRepository->findAllTeachers([$reservation->getTeacher()]) as $recipient) {
174
            $notification = new Notification(
175
                $recipient,
176
                $this->translator->trans('reservation.title', [], 'email'),
177
                $this->translator->trans('reservation.content', [
178
                    '%room%' => $reservation->getResource()->getName(),
179
                    '%date%' => $reservation->getDate()->format($this->translator->trans('date.format_short')),
180
                    '%lesson%' => $this->translator->trans('label.substitution_lessons', [
181
                        '%start%' => $reservation->getLessonStart(),
182
                        '%end%' => $reservation->getLessonEnd(),
183
                        '%count%' => ($reservation->getLessonEnd() - $reservation->getLessonStart())
184
                    ])
185
                ], 'email'),
186
                $this->urlGenerator->generate('edit_room_reservation', ['uuid' => $reservation->getUuid()->toString()], UrlGeneratorInterface::ABSOLUTE_URL),
187
                $this->translator->trans('reservation.link', [], 'email')
188
            );
189
190
            $this->notificationService->notify($notification);
191
        }
192
    }
193
194
    /**
195
     * @param ConstraintViolationInterface[] $violationList
196
     */
197
    private function sendExamViolationsEmail(Exam $exam, array $violationList): void {
0 ignored issues
show
Unused Code introduced by
The parameter $violationList is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

197
    private function sendExamViolationsEmail(Exam $exam, /** @scrutinizer ignore-unused */ array $violationList): void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
198
        $teachers = [ ];
199
200
        /** @var Tuition $tuition */
201
        foreach($exam->getTuitions() as $tuition) {
202
            foreach($tuition->getTeachers() as $teacher) {
203
                if(!in_array($teacher, $teachers)) {
204
                    $teachers[] = $teacher;
205
                }
206
            }
207
        }
208
209
        foreach($this->userRepository->findAllTeachers($teachers) as $recipient) {
210
            $notification = new Notification(
211
                $recipient,
212
                $this->translator->trans('reservation.title', [], 'email'),
213
                $this->translator->trans('reservation.content_exam', [
214
                    '%room%' => $exam->getRoom()?->getName(),
215
                    '%date%' => $exam->getDate()->format($this->translator->trans('date.format_short')),
216
                    '%lesson%' => $this->translator->trans('label.substitution_lessons', [
217
                        '%start%' => $exam->getLessonStart(),
218
                        '%end%' => $exam->getLessonEnd(),
219
                        '%count%' => ($exam->getLessonEnd() - $exam->getLessonStart())
220
                    ])
221
                ], 'email'),
222
                $this->urlGenerator->generate('edit_exam', ['uuid' => $exam->getUuid()->toString()], UrlGeneratorInterface::ABSOLUTE_URL),
223
                $this->translator->trans('reservation.link', [], 'email')
224
            );
225
226
            $this->notificationService->notify($notification);
227
        }
228
    }
229
230
    /**
231
     * @inheritDoc
232
     */
233
    public static function getSubscribedEvents(): array {
234
        return [
235
            SubstitutionImportEvent::class => 'onSubstitutionImportEvent'
236
        ];
237
    }
238
}