SubstitutionsImportStrategy::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 0
c 0
b 0
f 0
dl 0
loc 2
rs 10
ccs 0
cts 0
cp 0
cc 1
nc 1
nop 8
crap 2

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\Import;
4
5
use App\Entity\Grade;
6
use App\Entity\Room;
7
use App\Entity\Section;
8
use App\Entity\StudyGroup;
9
use App\Entity\Substitution;
10
use App\Entity\Teacher;
11
use App\Event\SubstitutionImportEvent;
12
use App\Repository\GradeRepositoryInterface;
13
use App\Repository\RoomRepositoryInterface;
14
use App\Repository\SectionRepositoryInterface;
15
use App\Repository\StudyGroupRepositoryInterface;
16
use App\Repository\SubstitutionRepositoryInterface;
17
use App\Repository\TeacherRepositoryInterface;
18
use App\Repository\TransactionalRepositoryInterface;
19
use App\Repository\TuitionRepositoryInterface;
20
use App\Request\Data\SubstitutionData;
21
use App\Request\Data\SubstitutionsData;
22
use App\Utils\ArrayUtils;
23
use App\Utils\CollectionUtils;
24
use DateTime;
25
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
26
27
class SubstitutionsImportStrategy implements ImportStrategyInterface, PostActionStrategyInterface {
28
29
    use ContextAwareTrait;
30
31
    public function __construct(private SubstitutionRepositoryInterface $substitutionRepository, private TeacherRepositoryInterface $teacherRepository, private StudyGroupRepositoryInterface $studyGroupRepository, private TuitionRepositoryInterface $tuitionRepository, private RoomRepositoryInterface $roomRepository, private GradeRepositoryInterface $gradeRepository, private SectionRepositoryInterface $sectionRepository, private EventDispatcherInterface $dispatcher)
32
    {
33
    }
34
35
    /**
36
     * @param SubstitutionsData $requestData
37
     * @return array<string, Substitution>
38
     * @throws ImportException
39
     */
40
    public function getExistingEntities($requestData): array {
41
        $dateTime = $this->getContext($requestData);
42
43
        if ($dateTime !== null) {
44
            $substitutions = $this->substitutionRepository->findAllByDate($dateTime);
45
        } else {
46
            $substitutions = $this->substitutionRepository->findAll();
47
        }
48
49
        return ArrayUtils::createArrayWithKeys(
50
            $substitutions,
51
            fn(Substitution $substitution) => $substitution->getExternalId()
52
        );
53
    }
54
55
    /**
56
     * @param SubstitutionData $data
57
     * @param SubstitutionsData $requestData
58
     * @return Substitution
59
     * @throws ImportException
60
     */
61
    public function createNewEntity($data, $requestData) {
62
        $substitution = (new Substitution())
63
            ->setExternalId($data->getId());
64
        $this->updateEntity($substitution, $data, $requestData);
65
66
        return $substitution;
67
    }
68
69
    /**
70
     * @param SubstitutionData $object
71
     * @param array<string, Substitution> $existingEntities
72
     * @return Substitution|null
73
     */
74
    public function getExistingEntity($object, array $existingEntities) {
75
        return $existingEntities[$object->getId()] ?? null;
76
    }
77
78
    /**
79
     * @param Substitution $entity
80
     */
81
    public function getEntityId($entity): int {
82
        return $entity->getId();
83
    }
84
85
    /**
86
     * @param Substitution $entity
87
     * @param SubstitutionData $data
88
     * @param SubstitutionsData $requestData
89
     * @throws ImportException
90
     * @throws SectionNotResolvableException
91
     */
92
    public function updateEntity($entity, $data, $requestData): void {
93
        $teacherIdSelector = fn(Teacher $teacher) => $teacher->getId();
94
95
        $teachers = $this->teacherRepository->findAllByExternalId($data->getTeachers());
96
97
        if(count($teachers) !== count($data->getTeachers())) {
98
            $this->throwMissingTeacher($data->getTeachers(), $teachers, $data->getId());
0 ignored issues
show
Bug introduced by
It seems like $data->getId() can also be of type null; however, parameter $substitutionId of App\Import\Substitutions...::throwMissingTeacher() does only seem to accept string, 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

98
            $this->throwMissingTeacher($data->getTeachers(), $teachers, /** @scrutinizer ignore-type */ $data->getId());
Loading history...
99
        }
100
101
        CollectionUtils::synchronize($entity->getTeachers(), $teachers, $teacherIdSelector);
102
103
        $replacementTeachers = $this->teacherRepository->findAllByExternalId($data->getReplacementTeachers());
104
105
        if(count($replacementTeachers) !== count($data->getReplacementTeachers())) {
106
            $this->throwMissingTeacher($data->getReplacementTeachers(), $replacementTeachers, $data->getId());
107
        }
108
109
        $section = $this->sectionRepository->findOneByDate($data->getDate());
110
111
        if($section === null) {
112
            throw new SectionNotResolvableException($data->getDate());
113
        }
114
115
        CollectionUtils::synchronize($entity->getReplacementTeachers(), $replacementTeachers, $teacherIdSelector);
116
117
        $entity->setDate($data->getDate());
118
        $entity->setLessonStart($data->getLessonStart());
119
        $entity->setLessonEnd($data->getLessonEnd());
120
        $entity->setSubject($data->getSubject());
121
        $entity->setReplacementSubject($data->getReplacementSubject());
122
        $entity->setRemark($data->getText());
123
        $entity->setType($data->getType());
124
        $entity->setStartsBefore($data->startsBefore());
125
126
        $rooms = $this->roomRepository->findAllByExternalIds($data->getRooms());
127
        CollectionUtils::synchronize(
128
            $entity->getRooms(),
129
            $rooms,
130
            fn(Room $room) => $room->getId()
131
        );
132
133
        if(count($rooms) > 0 || count($data->getRooms()) === 0) {
134
            $entity->setRoomName(null);
135
        } else {
136
            $entity->setRoomName(implode(', ', $data->getRooms()));
137
        }
138
139
        $replacementRooms = $this->roomRepository->findAllByExternalIds($data->getReplacementRooms());
140
        CollectionUtils::synchronize(
141
            $entity->getReplacementRooms(),
142
            $replacementRooms,
143
            fn(Room $room) => $room->getId()
144
        );
145
146
        if(count($replacementRooms) > 0 || count($data->getReplacementRooms()) === 0) {
147
            $entity->setReplacementRoomName(null);
148
        } else {
149
            $entity->setReplacementRoomName(implode(', ', $data->getReplacementRooms()));
150
        }
151
152
        $studyGroups = $this->resolveStudyGroup($section, $data->getSubject(), $data->getGrades(), $data->getTeachers(), $data->getId());
0 ignored issues
show
Bug introduced by
It seems like $data->getId() can also be of type null; however, parameter $id of App\Import\Substitutions...gy::resolveStudyGroup() does only seem to accept string, 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

152
        $studyGroups = $this->resolveStudyGroup($section, $data->getSubject(), $data->getGrades(), $data->getTeachers(), /** @scrutinizer ignore-type */ $data->getId());
Loading history...
153
154
        CollectionUtils::synchronize(
155
            $entity->getStudyGroups(),
156
            $studyGroups,
157
            fn(StudyGroup $studyGroup) => $studyGroup->getId()
158
        );
159
160
        if($data->getSubject() === $data->getReplacementSubject()) {
161
            $replacementStudyGroups = $studyGroups;
162
        } else {
163
            $replacementStudyGroups = $this->resolveStudyGroup($section, $data->getReplacementSubject(), $data->getReplacementGrades(), $data->getReplacementTeachers(), $data->getId());
164
        }
165
        
166
        CollectionUtils::synchronize(
167
            $entity->getReplacementStudyGroups(),
168
            $replacementStudyGroups,
169
            fn(StudyGroup $studyGroup) => $studyGroup->getId()
170
        );
171
172
        $replacementGrades = $this->gradeRepository->findAllByExternalId($data->getReplacementGrades());
173
174
        if(count($replacementGrades) != count($data->getReplacementGrades())) {
175
            $this->throwMissingGrade($data->getReplacementGrades(), $replacementGrades, $data->getId());
176
        }
177
178
        $replacementStudyGroupGrades = [ ];
179
        /** @var StudyGroup $studyGroup */
180
        foreach($entity->getReplacementStudyGroups() as $studyGroup) {
181
            foreach($studyGroup->getGrades() as $grade) {
182
                if(!in_array($grade, $replacementStudyGroupGrades)) {
183
                    $replacementStudyGroupGrades[] = $grade;
184
                }
185
            }
186
        }
187
188
        if(count($replacementGrades) !== count($replacementStudyGroupGrades)) {
189
            CollectionUtils::synchronize(
190
                $entity->getReplacementGrades(),
191
                $replacementGrades,
192
                fn(Grade $grade) => $grade->getId()
193
            );
194
        } else {
195
            $entity->getReplacementGrades()->clear();
196
        }
197
    }
198
199
    /**
200
     * @param string[] $grades
201
     * @param string[] $teachers
202
     * @return StudyGroup[]
203
     * @throws ImportException
204
     */
205
    private function resolveStudyGroup(Section $section, ?string $subject, array $grades, array $teachers, string $id): array {
206
        $result = [ ];
207
208
        if(empty($subject)) {
209
            foreach($grades as $grade) {
210
                $studyGroup = $this->studyGroupRepository->findOneByGradeName($grade, $section);
211
212
                if($studyGroup === null) {
213
                    throw new ImportException(sprintf('Lerngruppe für die Klasse "%s" bei der Vertretung mit ID "%s" wurde nicht gefunden.', $grade, $id));
214
                }
215
216
                $result[] = $studyGroup;
217
            }
218
        } else if(count($grades) > 0 && count($teachers) > 0) {
219
            $tuitions = $this->tuitionRepository->findAllByGradeTeacherAndSubjectOrCourse($grades, $teachers, $subject, $section);
220
221
            if(count($tuitions) === 1 && $tuitions[0]->getStudyGroup() !== null) {
222
                $result[] = $tuitions[0]->getStudyGroup();
223
            } else {
224
                return [ ];
225
            }
226
        }
227
228
        return $result;
229
    }
230
231
    /**
232
     * @param Substitution $entity
233
     */
234
    public function persist($entity): void {
235
        $this->substitutionRepository->persist($entity);
236
    }
237
238
    /**
239
     * @param Substitution $entity
240
     */
241
    public function remove($entity, $requestData): bool {
242
        $this->substitutionRepository->remove($entity);
243
        return true;
244
    }
245
246
    /**
247
     * @inheritDoc
248
     */
249
    public function getRepository(): TransactionalRepositoryInterface {
250
        return $this->substitutionRepository;
251
    }
252
253
    /**
254
     * @param string[] $teachers
255
     * @param Teacher[] $foundTeachers
256
     * @throws ImportException
257
     */
258
    private function throwMissingTeacher(array $teachers, array $foundTeachers, string $substitutionId) {
259
        $foundTeacherExternalIds = array_map(fn(Teacher $teacher) => $teacher->getExternalId(), $foundTeachers);
260
261
        foreach($teachers as $teacher) {
262
            if(!in_array($teacher, $foundTeacherExternalIds)) {
263
                throw new ImportException(sprintf('Lehrkraft "%s" bei Vertretung "%s" wurde nicht gefunden.', $teacher, $substitutionId));
264
            }
265
        }
266
    }
267
268
    /**
269
     * @param string[]$grades
270
     * @param Grade[] $foundGrades
271
     * @throws ImportException
272
     */
273
    private function throwMissingGrade(array $grades, array $foundGrades, string $substitutionId) {
274
        $foundGradeExternalIds = array_map(fn(Grade $grade) => $grade->getExternalId(), $foundGrades);
275
276
        foreach($grades as $grade) {
277
            if(!in_array($grade, $foundGradeExternalIds)) {
278
                throw new ImportException(sprintf('Klasse "%s" bei Vertretung "%s" wurde nicht gefunden.', $grade, $substitutionId));
279
            }
280
        }
281
    }
282
283
    /**
284
     * @param SubstitutionsData $data
285
     * @return SubstitutionData[]
286
     */
287
    public function getData($data): array {
288
        return $data->getSubstitutions();
289
    }
290
291
    /**
292
     * @inheritDoc
293
     */
294
    public function getEntityClassName(): string {
295
        return Substitution::class;
296
    }
297
298
    public function onFinished(ImportResult $result) {
299
        /** @var SubstitutionsData $request */
300
        $request = $result->getRequest();
301
302
        if($request->isSuppressNotifications() === false) {
303
            $this->dispatcher->dispatch(new SubstitutionImportEvent($result->getAdded(), $result->getUpdated(), $result->getRemoved()));
304
        }
305
    }
306
}