SubstitutionsImportStrategy::updateEntity()   F
last analyzed

Complexity

Conditions 14
Paths 516

Size

Total Lines 104
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 0
Metric Value
cc 14
eloc 66
c 0
b 0
f 0
nc 516
nop 3
dl 0
loc 104
rs 2.7722
ccs 0
cts 56
cp 0
crap 210

How to fix   Long Method    Complexity   

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:

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
}