TimetableController::index()   F
last analyzed

Complexity

Conditions 30
Paths 253

Size

Total Lines 142
Code Lines 99

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 930

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 30
eloc 99
c 3
b 0
f 0
nc 253
nop 13
dl 0
loc 142
rs 2.6708
ccs 0
cts 86
cp 0
crap 930

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Controller;
4
5
use App\Security\Voter\TimetableVoter;
6
use Symfony\Component\HttpFoundation\Response;
7
use App\Date\WeekOfYear;
8
use App\Entity\IcsAccessToken;
9
use App\Entity\IcsAccessTokenType;
10
use App\Entity\MessageScope;
11
use App\Entity\StudyGroupMembership;
12
use App\Entity\Subject;
13
use App\Entity\TimetableLesson;
14
use App\Entity\TimetableSupervision;
15
use App\Entity\User;
16
use App\Export\TimetableIcsExporter;
17
use App\Form\IcsAccessTokenType as DeviceTokenTypeForm;
18
use App\Grouping\Grouper;
19
use App\Message\DismissedMessagesHelper;
20
use App\Repository\ImportDateTypeRepositoryInterface;
21
use App\Repository\MessageRepositoryInterface;
22
use App\Repository\SubjectRepositoryInterface;
23
use App\Repository\TimetableLessonRepositoryInterface;
24
use App\Repository\TimetableSupervisionRepositoryInterface;
25
use App\Repository\UserRepositoryInterface;
26
use App\Security\IcsAccessToken\IcsAccessTokenManager;
27
use App\Settings\TimetableSettings;
28
use App\Sorting\Sorter;
29
use App\Timetable\TimetableFilter;
30
use App\Timetable\TimetableHelper;
31
use App\Utils\ArrayUtils;
32
use App\View\Filter\GradeFilter;
33
use App\View\Filter\RoomFilter;
34
use App\View\Filter\SectionFilter;
35
use App\View\Filter\StudentFilter;
36
use App\View\Filter\SubjectsFilter;
37
use App\View\Filter\TeachersFilter;
38
use DateTime;
39
use SchulIT\CommonBundle\Helper\DateHelper;
40
use SchulIT\CommonBundle\Utils\RefererHelper;
41
use Symfony\Component\HttpFoundation\Request;
42
use Symfony\Component\Routing\Annotation\Route;
43
44
#[Route(path: '/timetable')]
45
class TimetableController extends AbstractControllerWithMessages {
46
47
    private const OnlyOneWeek = 'timetable.only_one_week';
48
49
    use RequestTrait;
50
    use CalendarWeeksTrait;
0 ignored issues
show
Bug introduced by
The trait App\Controller\CalendarWeeksTrait requires the property $query which is not provided by App\Controller\TimetableController.
Loading history...
51
52
    public function __construct(MessageRepositoryInterface $messageRepository, DismissedMessagesHelper $dismissedMessagesHelper,
53
                                DateHelper $dateHelper, private TimetableHelper $timetableHelper, private TimetableSettings $timetableSettings,
54
                                RefererHelper $refererHelper) {
55
        parent::__construct($messageRepository, $dismissedMessagesHelper, $dateHelper, $refererHelper);
56
    }
57
58
    #[Route(path: '', name: 'timetable')]
59
    public function index(StudentFilter $studentFilter, TeachersFilter $teachersFilter, GradeFilter $gradeFilter, RoomFilter $roomFilter, SubjectsFilter $subjectFilter,
60
                          TimetableLessonRepositoryInterface $lessonRepository, TimetableSupervisionRepositoryInterface $supervisionRepository, TimetableFilter $timetableFilter, ImportDateTypeRepositoryInterface $importDateTypeRepository,
61
                          SubjectRepositoryInterface $subjectRepository, SectionFilter $sectionFilter, Request $request, UserRepositoryInterface $userRepository): Response {
62
        /** @var User $user */
63
        $user = $this->getUser();
64
65
        if($request->isMethod('POST')) {
66
            $onlyOneWeek = $request->request->getBoolean('only_one_week');
67
            $user->setData(self::OnlyOneWeek, $onlyOneWeek);
68
69
            $userRepository->persist($user);
70
            return $this->redirectToRoute('timetable', $request->query->all());
71
        }
72
73
        $sectionFilterView = $sectionFilter->handle($request->query->get('section', null));
74
        $gradeFilterView = $gradeFilter->handle($request->query->get('grade', null), $sectionFilterView->getCurrentSection(), $user);
75
        $roomFilterView = $roomFilter->handle($request->query->get('room', null), $user);
76
        $subjectFilterView = $subjectFilter->handle($this->getArrayOrNull($request->query->all('subjects')), $user);
77
        $studentFilterView = $studentFilter->handle($request->query->get('student', null), $sectionFilterView->getCurrentSection(), $user, $gradeFilterView->getCurrentGrade() === null && $roomFilterView->getCurrentRoom() === null && (is_countable($subjectFilterView->getCurrentSubjects()) ? count($subjectFilterView->getCurrentSubjects()) : 0) === 0);
78
        $teachersFilterView = $teachersFilter->handle($this->getArrayOrNull($request->query->all('teachers')), $sectionFilterView->getCurrentSection(), $user, $studentFilterView->getCurrentStudent() === null && $gradeFilterView->getCurrentGrade() === null && $roomFilterView->getCurrentRoom() === null && (is_countable($subjectFilterView->getCurrentSubjects()) ? count($subjectFilterView->getCurrentSubjects()) : 0) === 0);
79
80
        $selectedDate = $this->resolveSelectedDate($request, $sectionFilterView->getCurrentSection(), $this->dateHelper);
81
82
        $start = max(
83
            $selectedDate,
84
            $startDate = $this->timetableSettings->getStartDate($user->getUserType())
85
        );
86
        $end = min(
87
            (clone $start)->modify('+13 days'),
88
            $endDate = $this->timetableSettings->getEndDate($user->getUserType())
89
        );
90
91
        $lessons = [ ];
92
        $supervisions = [ ];
93
        $membershipsTypes = [ ];
94
95
        if($start <= $end) {
96
            if ($studentFilterView->getCurrentStudent() !== null) {
97
                $lessons = $lessonRepository->findAllByStudent($start, $end, $studentFilterView->getCurrentStudent());
98
                $lessons = $timetableFilter->filterStudentLessons($lessons);
99
100
                $gradeIdsWithMembershipTypes = $this->timetableSettings->getGradeIdsWithMembershipTypes();
101
102
                /** @var StudyGroupMembership $membership */
103
                foreach($studentFilterView->getCurrentStudent()->getStudyGroupMemberships() as $membership) {
104
                    foreach($membership->getStudyGroup()->getGrades() as $grade) {
105
                        if (in_array($grade->getId(), $gradeIdsWithMembershipTypes)) {
106
                            $membershipsTypes[$membership->getStudyGroup()->getId()] = $membership->getType();
107
                        }
108
                    }
109
                }
110
            } else if (count($teachersFilterView->getCurrentTeachers()) > 0) {
111
                $lessons = [ ];
112
                $supervisions = [ ];
113
114
                foreach($teachersFilterView->getCurrentTeachers() as $teacher) {
115
                    $lessons = array_merge($lessons, $timetableFilter->filterTeacherLessons($lessonRepository->findAllByTeacher($start, $end, $teacher)));
116
                    $supervisions = array_merge($supervisions, $supervisionRepository->findAllByTeacher($start, $end, $teacher));
117
                }
118
            } else if ($gradeFilterView->getCurrentGrade() !== null) {
119
                $lessons = $lessonRepository->findAllByGrade($start, $end, $gradeFilterView->getCurrentGrade());
120
                $lessons = $timetableFilter->filterGradeLessons($lessons);
121
            } else if ($roomFilterView->getCurrentRoom() !== null) {
122
                $lessons = $lessonRepository->findAllByRoom($start, $end, $roomFilterView->getCurrentRoom());
123
                $lessons = $timetableFilter->filterRoomLessons($lessons);
124
            } else if ((is_countable($subjectFilterView->getSubjects()) ? count($subjectFilterView->getSubjects()) : 0) > 0) {
125
                $lessons = $lessonRepository->findAllBySubjects($start, $end, $subjectFilterView->getCurrentSubjects());
126
                $lessons = $timetableFilter->filterSubjectsLessons($lessons);
127
            }
128
        }
129
130
        if((is_countable($lessons) ? count($lessons) : 0) === 0 && count($supervisions) === 0) {
131
            $timetable = null;
132
        } else {
133
            $weeks = $this->getWeeksToDisplay($start, $end, $user->getData(self::OnlyOneWeek, false));
134
            $timetable = $this->timetableHelper->makeTimetable($weeks, $lessons, $supervisions);
135
        }
136
137
        $startTimes = [ ];
138
        $endTimes = [ ];
139
140
        for($lesson = 1; $lesson <= $this->timetableSettings->getMaxLessons(); $lesson++) {
141
            $startTimes[$lesson] = $this->timetableSettings->getStart($lesson);
142
            $endTimes[$lesson] = $this->timetableSettings->getEnd($lesson);
143
        }
144
145
        $template = 'timetable/index.html.twig';
146
147
        if($request->query->getBoolean('print', false) === true) {
148
            $template = 'timetable/index_print.html.twig';
149
150
            if($timetable === null) {
151
                $query = $request->query->all();
152
                unset($query['print']);
153
                $this->addFlash('info', 'plans.timetable.print.empty');
154
                return $this->redirectToRoute('timetable', $query);
155
            }
156
        }
157
158
        $supervisionLabels = [ ];
159
        for($i = 1; $i <= $this->timetableSettings->getMaxLessons(); $i++) {
160
            $supervisionLabels[$i] = $this->timetableSettings->getDescriptionBeforeLesson($i);
161
        }
162
163
        $subjects = ArrayUtils::createArrayWithKeys(
164
            $subjectRepository->findAll(),
165
            fn(Subject $subject) => $subject->getAbbreviation()
166
        );
167
168
        $weekStarts = [ ];
169
170
        if($startDate !== null && $endDate !== null && $sectionFilterView->getCurrentSection() !== null) {
171
            $weekStarts = $this->listCalendarWeeks(
172
                max($startDate, $sectionFilterView->getCurrentSection()->getStart()),
173
                min($endDate, $sectionFilterView->getCurrentSection()->getEnd())
174
            );
175
        }
176
177
        return $this->renderWithMessages($template, [
178
            'compact' => count($teachersFilterView->getCurrentTeachers()) > 1,
179
            'timetable' => $timetable,
180
            'studentFilter' => $studentFilterView,
181
            'teachersFilter' => $teachersFilterView,
182
            'gradeFilter' => $gradeFilterView,
183
            'roomFilter'=> $roomFilterView,
184
            'subjectFilter' => $subjectFilterView,
185
            'sectionFilter' => $sectionFilterView,
186
            'startTimes' => $startTimes,
187
            'endTimes' => $endTimes,
188
            'gradesWithCourseNames' => $this->timetableSettings->getGradeIdsWithCourseNames(),
189
            'memberships' => $membershipsTypes,
190
            'query' => $request->query->all(),
191
            'supervisionLabels' => $supervisionLabels,
192
            'supervisionSubject' => $this->timetableSettings->getSupervisionLabel(),
193
            'supervisionColor' => $this->timetableSettings->getSupervisionColor(),
194
            'last_import_lessons' => $importDateTypeRepository->findOneByEntityClass(TimetableLesson::class),
195
            'last_import_supervisions' => $importDateTypeRepository->findOneByEntityClass(TimetableSupervision::class),
196
            'subjects' => $subjects,
197
            'selectedDate' => $selectedDate,
198
            'weekStarts' => $weekStarts,
199
            'onlyOneWeek' => $user->getData(self::OnlyOneWeek, false)
200
        ]);
201
    }
202
203
    /**
204
     * @param DateTime $start
205
     * @param DateTime $end
206
     * @param bool $onlyOneWeek
207
     * @return WeekOfYear[]
208
     */
209
    private function getWeeksToDisplay(DateTime $start, DateTime $end, bool $onlyOneWeek): array {
210
        $weeks = [
211
            (new WeekOfYear((int)$start->format('Y'), (int)$start->format('W'))),
212
            (new WeekOfYear((int)$end->format('Y'), (int)$end->format('W')))
213
        ];
214
215
        if($weeks[0]->getWeekNumber() === $weeks[1]->getWeekNumber() && $weeks[0]->getYear() === $weeks[1]->getYear()) {
216
            $weeks = [ $weeks[0] ];
217
        }
218
219
        if($onlyOneWeek === true) {
220
            $weeks = [ $weeks[0] ];
221
        }
222
223
        return $weeks;
224
    }
225
226
    #[Route(path: '/supervisions', name: 'timetable_supervisions')]
227
    public function supervisions(TimetableSupervisionRepositoryInterface $supervisionRepository, TimetableFilter $timetableFilter, ImportDateTypeRepositoryInterface $importDateTypeRepository,
0 ignored issues
show
Unused Code introduced by
The parameter $timetableFilter 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

227
    public function supervisions(TimetableSupervisionRepositoryInterface $supervisionRepository, /** @scrutinizer ignore-unused */ TimetableFilter $timetableFilter, ImportDateTypeRepositoryInterface $importDateTypeRepository,

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...
228
                                 SectionFilter $sectionFilter, Request $request, UserRepositoryInterface $userRepository) {
229
        $this->denyAccessUnlessGranted(TimetableVoter::Supervisions);
230
231
        /** @var User $user */
232
        $user = $this->getUser();
233
234
        if($request->isMethod('POST')) {
235
            $onlyOneWeek = $request->request->getBoolean('only_one_week');
236
            $user->setData(self::OnlyOneWeek, $onlyOneWeek);
237
238
            $userRepository->persist($user);
239
            return $this->redirectToRoute('timetable_supervisions', $request->query->all());
240
        }
241
242
        $sectionFilterView = $sectionFilter->handle($request->query->get('section', null));
243
        $selectedDate = $this->resolveSelectedDate($request, $sectionFilterView->getCurrentSection(), $this->dateHelper);
244
245
        $start = max(
246
            $selectedDate,
247
            $startDate = $this->timetableSettings->getStartDate($user->getUserType())
248
        );
249
        $end = min(
250
            (clone $start)->modify('+13 days'),
251
            $endDate = $this->timetableSettings->getEndDate($user->getUserType())
252
        );
253
254
        $timetable = null;
255
256
        if($start <= $end) {
257
            $supervisions = $supervisionRepository->findAllByRange($start, $end);
258
            $weeks = $this->getWeeksToDisplay($start, $end, $user->getData(self::OnlyOneWeek, false));
259
260
            if(count($supervisions) > 0) {
261
                $timetable = $this->timetableHelper->makeTimetable($weeks, [], $supervisions);
262
            }
263
        }
264
265
        $startTimes = [ ];
266
        $endTimes = [ ];
267
268
        for($lesson = 1; $lesson <= $this->timetableSettings->getMaxLessons(); $lesson++) {
269
            $startTimes[$lesson] = $this->timetableSettings->getStart($lesson);
270
            $endTimes[$lesson] = $this->timetableSettings->getEnd($lesson);
271
        }
272
273
        $template = 'timetable/index.html.twig';
0 ignored issues
show
Unused Code introduced by
The assignment to $template is dead and can be removed.
Loading history...
274
275
        if($request->query->getBoolean('print', false) === true) {
276
            $template = 'timetable/index_print.html.twig';
277
278
            if($timetable === null) {
279
                $query = $request->query->all();
280
                unset($query['print']);
281
                $this->addFlash('info', 'plans.timetable.print.empty');
282
                return $this->redirectToRoute('timetable', $query);
283
            }
284
        }
285
286
        $supervisionLabels = [ ];
287
        for($i = 1; $i <= $this->timetableSettings->getMaxLessons(); $i++) {
288
            $supervisionLabels[$i] = $this->timetableSettings->getDescriptionBeforeLesson($i);
289
        }
290
291
        $weekStarts = [ ];
292
293
        if($startDate !== null && $endDate !== null && $sectionFilterView->getCurrentSection() !== null) {
294
            $weekStarts = $this->listCalendarWeeks(
295
                max($startDate, $sectionFilterView->getCurrentSection()->getStart()),
296
                min($endDate, $sectionFilterView->getCurrentSection()->getEnd())
297
            );
298
        }
299
300
        return $this->renderWithMessages('timetable/supervisions.html.twig', [
301
            'timetable' => $timetable,
302
            'sectionFilter' => $sectionFilterView,
303
            'startTimes' => $startTimes,
304
            'endTimes' => $endTimes,
305
            'query' => $request->query->all(),
306
            'supervisionLabels' => $supervisionLabels,
307
            'supervisionSubject' => $this->timetableSettings->getSupervisionLabel(),
308
            'supervisionColor' => $this->timetableSettings->getSupervisionColor(),
309
            'last_import_lessons' => $importDateTypeRepository->findOneByEntityClass(TimetableLesson::class),
310
            'last_import_supervisions' => $importDateTypeRepository->findOneByEntityClass(TimetableSupervision::class),
311
            'selectedDate' => $selectedDate,
312
            'weekStarts' => $weekStarts,
313
            'onlyOneWeek' => $user->getData(self::OnlyOneWeek, false)
314
        ]);
315
316
    }
317
318
    #[Route(path: '/export', name: 'timetable_export')]
319
    public function export(Request $request, IcsAccessTokenManager $manager): Response {
320
        /** @var User $user */
321
        $user = $this->getUser();
322
323
        $deviceToken = (new IcsAccessToken())
324
            ->setType(IcsAccessTokenType::Timetable)
325
            ->setUser($user);
326
327
        $form = $this->createForm(DeviceTokenTypeForm::class, $deviceToken);
328
        $form->handleRequest($request);
329
330
        if($form->isSubmitted() && $form->isValid()) {
331
            $deviceToken = $manager->persistToken($deviceToken);
332
        }
333
334
        return $this->renderWithMessages('timetable/export.html.twig', [
335
            'form' => $form->createView(),
336
            'token' => $deviceToken
337
        ]);
338
    }
339
340
    #[Route(path: '/ics/download', name: 'timetable_ics')]
341
    #[Route(path: '/ics/downloads/{token}', name: 'timetable_ics_token')]
342
    public function ics(TimetableIcsExporter $icsExporter): Response {
343
        /** @var User $user */
344
        $user = $this->getUser();
345
346
        return $icsExporter->getIcsResponse($user);
347
    }
348
349
    protected function getMessageScope(): MessageScope {
350
        return MessageScope::Timetable;
351
    }
352
}