Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
140 / 140 |
|
100.00% |
6 / 6 |
CRAP | |
100.00% |
1 / 1 |
ShiftsController | |
100.00% |
140 / 140 |
|
100.00% |
6 / 6 |
19 | |
100.00% |
1 / 1 |
entriesByAngeltype | |
100.00% |
29 / 29 |
|
100.00% |
1 / 1 |
1 | |||
entriesByLocation | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
1 | |||
entriesByShiftType | |
100.00% |
20 / 20 |
|
100.00% |
1 / 1 |
1 | |||
entriesByUser | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
shiftEntriesResponse | |
100.00% |
39 / 39 |
|
100.00% |
1 / 1 |
8 | |||
getNeededAngelTypes | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Engelsystem\Controllers\Api; |
6 | |
7 | use Engelsystem\Controllers\Api\Resources\AngelTypeResource; |
8 | use Engelsystem\Controllers\Api\Resources\LocationResource; |
9 | use Engelsystem\Controllers\Api\Resources\ShiftWithEntriesResource; |
10 | use Engelsystem\Controllers\Api\Resources\UserResource; |
11 | use Engelsystem\Http\Request; |
12 | use Engelsystem\Http\Response; |
13 | use Engelsystem\Models\AngelType; |
14 | use Engelsystem\Models\Location; |
15 | use Engelsystem\Models\Shifts\NeededAngelType; |
16 | use Engelsystem\Models\Shifts\Shift; |
17 | use Engelsystem\Models\Shifts\ShiftEntry; |
18 | use Engelsystem\Models\Shifts\ShiftType; |
19 | use Illuminate\Contracts\Database\Query\Builder as BuilderContract; |
20 | use Illuminate\Database\Eloquent\Collection; |
21 | |
22 | class ShiftsController extends ApiController |
23 | { |
24 | use UsesAuth; |
25 | |
26 | public function entriesByAngeltype(Request $request): Response |
27 | { |
28 | $id = (int) $request->getAttribute('angeltype_id'); |
29 | /** @var AngelType $angelType */ |
30 | $angelType = AngelType::findOrFail($id); |
31 | |
32 | // From users assigned to shifts |
33 | $shiftsByEntries = Shift::query() |
34 | ->join('shift_entries', 'shift_entries.shift_id', 'shifts.id') |
35 | ->where('angel_type_id', $angelType->id) |
36 | ->select('shifts.*'); |
37 | |
38 | // Needed by a shift directly |
39 | $shiftsByShift = Shift::query() |
40 | ->join('needed_angel_types', 'needed_angel_types.shift_id', 'shifts.id') |
41 | ->where('needed_angel_types.angel_type_id', $angelType->id) |
42 | ->select('shifts.*'); |
43 | |
44 | // Needed by connected schedule via shift location |
45 | $shiftsByScheduleLocation = Shift::query() |
46 | ->join('schedule_shift', 'schedule_shift.shift_id', 'shifts.id') |
47 | ->join('schedules', 'schedules.id', 'schedule_shift.schedule_id') |
48 | ->where('schedules.needed_from_shift_type', false) |
49 | ->join('needed_angel_types', 'needed_angel_types.location_id', 'shifts.location_id') |
50 | ->where('needed_angel_types.angel_type_id', $angelType->id) |
51 | ->select('shifts.*'); |
52 | |
53 | // Needed by connected schedule via schedule shift type |
54 | $shiftsByScheduleShiftType = Shift::query() |
55 | ->join('schedule_shift', 'schedule_shift.shift_id', 'shifts.id') |
56 | ->join('schedules', 'schedules.id', 'schedule_shift.schedule_id') |
57 | ->where('schedules.needed_from_shift_type', true) |
58 | ->join('needed_angel_types', 'needed_angel_types.shift_type_id', 'schedules.shift_type') |
59 | ->where('needed_angel_types.angel_type_id', $angelType->id) |
60 | ->select('shifts.*'); |
61 | |
62 | $shifts = $shiftsByShift |
63 | ->union($shiftsByEntries) |
64 | ->union($shiftsByScheduleLocation) |
65 | ->union($shiftsByScheduleShiftType); |
66 | |
67 | return $this->shiftEntriesResponse($shifts); |
68 | } |
69 | |
70 | public function entriesByLocation(Request $request): Response |
71 | { |
72 | $locationId = (int) $request->getAttribute('location_id'); |
73 | /** @var Location $location */ |
74 | $location = Location::findOrFail($locationId); |
75 | |
76 | // Needed by a shift directly |
77 | $shiftsByShift = $location->shifts(); |
78 | |
79 | // Needed by connected schedule via shift location |
80 | $shiftByScheduleLocation = Shift::query() |
81 | ->where('shifts.location_id', $location->id) |
82 | ->join('schedule_shift', 'schedule_shift.shift_id', 'shifts.id') |
83 | ->join('schedules', 'schedules.id', 'schedule_shift.schedule_id') |
84 | ->where('schedules.needed_from_shift_type', false) |
85 | ->join('needed_angel_types', 'needed_angel_types.location_id', 'shifts.location_id') |
86 | ->select('shifts.*'); |
87 | |
88 | // Needed by connected schedule via schedule shift type |
89 | $shiftsByScheduleShiftType = Shift::query() |
90 | ->where('shifts.location_id', $location->id) |
91 | ->join('schedule_shift', 'schedule_shift.shift_id', 'shifts.id') |
92 | ->join('schedules', 'schedules.id', 'schedule_shift.schedule_id') |
93 | ->where('schedules.needed_from_shift_type', true) |
94 | ->join('needed_angel_types', 'needed_angel_types.shift_type_id', 'schedules.shift_type') |
95 | ->select('shifts.*'); |
96 | |
97 | $shifts = $shiftsByShift |
98 | ->union($shiftByScheduleLocation) |
99 | ->union($shiftsByScheduleShiftType); |
100 | |
101 | return $this->shiftEntriesResponse($shifts); |
102 | } |
103 | |
104 | public function entriesByShiftType(Request $request): Response |
105 | { |
106 | $shiftTypeId = (int) $request->getAttribute('shifttype_id'); |
107 | /** @var ShiftType $shiftType */ |
108 | $shiftType = ShiftType::findOrFail($shiftTypeId); |
109 | |
110 | // Needed by a shift directly |
111 | $shiftsByShift = $shiftType->shifts(); |
112 | |
113 | // Needed by connected schedule via shift location |
114 | $shiftsByScheduleLocation = Shift::query() |
115 | ->join('schedule_shift', 'schedule_shift.shift_id', 'shifts.id') |
116 | ->join('schedules', 'schedules.id', 'schedule_shift.schedule_id') |
117 | ->where('schedules.needed_from_shift_type', false) |
118 | ->where('schedules.shift_type', $shiftType->id) |
119 | ->join('needed_angel_types', 'needed_angel_types.location_id', 'shifts.location_id') |
120 | ->select('shifts.*'); |
121 | |
122 | // Needed by connected schedule via schedule shift type |
123 | $shiftsByScheduleShiftType = Shift::query() |
124 | ->join('schedule_shift', 'schedule_shift.shift_id', 'shifts.id') |
125 | ->join('schedules', 'schedules.id', 'schedule_shift.schedule_id') |
126 | ->where('schedules.needed_from_shift_type', true) |
127 | ->where('schedules.shift_type', $shiftType->id) |
128 | ->select('shifts.*'); |
129 | |
130 | $shifts = $shiftsByShift |
131 | ->union($shiftsByScheduleLocation) |
132 | ->union($shiftsByScheduleShiftType); |
133 | |
134 | return $this->shiftEntriesResponse($shifts); |
135 | } |
136 | |
137 | public function entriesByUser(Request $request): Response |
138 | { |
139 | $id = $request->getAttribute('user_id'); |
140 | $user = $this->getUser($id); |
141 | |
142 | $shifts = Shift::query() |
143 | ->join('shift_entries', 'shift_entries.shift_id', 'shifts.id') |
144 | ->where('shift_entries.user_id', $user->id) |
145 | ->groupBy('shifts.id') |
146 | ->select('shifts.*'); |
147 | |
148 | return $this->shiftEntriesResponse($shifts); |
149 | } |
150 | |
151 | protected function shiftEntriesResponse(BuilderContract $shifts): Response |
152 | { |
153 | $shifts = $shifts |
154 | ->with([ |
155 | 'neededAngelTypes.angelType', |
156 | 'location.neededAngelTypes.angelType', |
157 | 'shiftEntries.angelType', |
158 | 'shiftEntries.user.contact', |
159 | 'shiftEntries.user.personalData', |
160 | 'shiftType', |
161 | 'scheduleShift', |
162 | 'schedule.shiftType.neededAngelTypes.angelType', |
163 | ]) |
164 | ->orderBy('start') |
165 | ->get(); |
166 | /** @var Shift[]|Collection $shifts */ |
167 | |
168 | $shiftEntries = []; |
169 | // Blob of not-optimized mediocre pseudo-serialization |
170 | foreach ($shifts as $shift) { |
171 | // Get all needed/used angel types |
172 | /** @var Collection|NeededAngelType[] $neededAngelTypes */ |
173 | $neededAngelTypes = $this->getNeededAngelTypes($shift); |
174 | |
175 | if ($neededAngelTypes->isEmpty()) { |
176 | continue; |
177 | } |
178 | |
179 | $angelTypes = new Collection(); |
180 | foreach ($neededAngelTypes as $neededAngelType) { |
181 | $entries = $neededAngelType->entries ?: new Collection(); |
182 | |
183 | // Skip empty entries |
184 | if ($neededAngelType->count <= 0 && $entries->isEmpty()) { |
185 | continue; |
186 | } |
187 | |
188 | $entries = $entries->map(fn(ShiftEntry $entry) => [ |
189 | 'user' => UserResource::toIdentifierArray($entry->user), |
190 | 'freeloaded_by' => $entry->freeloaded_by |
191 | ? UserResource::toIdentifierArray($entry->freeloadedBy) |
192 | : null, |
193 | ]); |
194 | $angelTypeData = AngelTypeResource::toIdentifierArray($neededAngelType->angelType); |
195 | $angelTypes[] = new Collection([ |
196 | 'angel_type' => $angelTypeData, |
197 | 'needs' => $neededAngelType->count, |
198 | 'entries' => $entries, |
199 | ]); |
200 | } |
201 | |
202 | $locationData = new LocationResource($shift->location); |
203 | $shiftEntries[] = (new ShiftWithEntriesResource($shift))->toArray($locationData, $angelTypes); |
204 | } |
205 | |
206 | $data = ['data' => $shiftEntries]; |
207 | return $this->response |
208 | ->withContent(json_encode($data)); |
209 | } |
210 | |
211 | /** |
212 | * Collect all needed angel types |
213 | */ |
214 | protected function getNeededAngelTypes(Shift $shift): Collection |
215 | { |
216 | $neededAngelTypes = new Collection(); |
217 | if (!$shift->schedule) { |
218 | // Get from shift |
219 | $neededAngelTypes = $shift->neededAngelTypes; |
220 | } elseif ($shift->schedule->needed_from_shift_type) { |
221 | // Load instead from shift type |
222 | $neededAngelTypes = $shift->schedule->shiftType->neededAngelTypes; |
223 | } elseif (!$shift->schedule->needed_from_shift_type) { |
224 | // Load instead from location |
225 | $neededAngelTypes = $shift->location->neededAngelTypes; |
226 | } |
227 | |
228 | // Create new instances of needed angel types to allow extension with entries per shift |
229 | $neededAngelTypes = $neededAngelTypes->map(function ($value) { |
230 | return clone $value; |
231 | }); |
232 | |
233 | // Add entries and additional angel types from manually added users |
234 | foreach ($shift->shiftEntries as $entry) { |
235 | // Ensure that angel type exists in list; add it if not |
236 | $neededAngelType = $neededAngelTypes->where('angel_type_id', $entry->angelType->id)->first(); |
237 | if (!$neededAngelType) { |
238 | $neededAngelType = new NeededAngelType([ |
239 | 'shift_id' => $shift->id, |
240 | 'angel_type_id' => $entry->angelType->id, |
241 | 'count' => 0, |
242 | ]); |
243 | $neededAngelTypes[] = $neededAngelType; |
244 | } |
245 | |
246 | // Initialize entries attribute for manually added users |
247 | if (!isset($neededAngelType->entries)) { |
248 | $neededAngelType->entries = new Collection(); |
249 | } |
250 | |
251 | // Add entries to needed angel type |
252 | $neededAngelType->entries[] = $entry; |
253 | } |
254 | |
255 | return $neededAngelTypes; |
256 | } |
257 | } |