Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
83 / 83
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
ShiftTypesController
100.00% covered (success)
100.00%
83 / 83
100.00% covered (success)
100.00%
6 / 6
13
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 edit
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 view
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 save
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
1 / 1
7
 delete
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Controllers\Admin;
6
7use Engelsystem\Controllers\BaseController;
8use Engelsystem\Controllers\HasUserNotifications;
9use Engelsystem\Http\Exceptions\ValidationException;
10use Engelsystem\Http\Redirector;
11use Engelsystem\Http\Request;
12use Engelsystem\Http\Response;
13use Engelsystem\Http\Validation\Validator;
14use Engelsystem\Models\AngelType;
15use Engelsystem\Models\Shifts\NeededAngelType;
16use Engelsystem\Models\Shifts\ShiftType;
17use Illuminate\Database\Eloquent\Collection;
18use Psr\Log\LoggerInterface;
19
20class ShiftTypesController extends BaseController
21{
22    use HasUserNotifications;
23
24    /** @var array<string> */
25    protected array $permissions = [
26        'shifttypes.view',
27        'edit' => 'shifttypes.edit',
28        'delete' => 'shifttypes.edit',
29        'save' => 'shifttypes.edit',
30    ];
31
32    public function __construct(
33        protected LoggerInterface $log,
34        protected ShiftType $shiftType,
35        protected Redirector $redirect,
36        protected Response $response
37    ) {
38    }
39
40    public function index(): Response
41    {
42        $shiftTypes = $this->shiftType
43            ->get()
44            ->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE);
45
46        return $this->response->withView(
47            'admin/shifttypes/index',
48            ['shifttypes' => $shiftTypes, 'is_index' => true]
49        );
50    }
51
52    public function edit(Request $request): Response
53    {
54        $shiftTypeId = (int) $request->getAttribute('shift_type_id');
55
56        $shiftType = $this->shiftType->find($shiftTypeId);
57        $angeltypes = AngelType::all()
58            ->sortBy('name');
59
60        return $this->response->withView(
61            'admin/shifttypes/edit',
62            [
63                'shifttype' => $shiftType,
64                'angel_types' => $angeltypes,
65            ]
66        );
67    }
68
69    public function view(Request $request): Response
70    {
71        $shiftTypeId = (int) $request->getAttribute('shift_type_id');
72        $shiftType = $this->shiftType->findOrFail($shiftTypeId);
73
74        return $this->response->withView(
75            'admin/shifttypes/view',
76            ['shifttype' => $shiftType, 'is_view' => true]
77        );
78    }
79
80    public function save(Request $request): Response
81    {
82        $shiftTypeId = (int) $request->getAttribute('shift_type_id');
83
84        /** @var ShiftType $shiftType */
85        $shiftType = $this->shiftType->findOrNew($shiftTypeId);
86
87        if ($request->request->has('delete')) {
88            return $this->delete($request);
89        }
90
91        /** @var Collection|AngelType[] $angelTypes */
92        $angelTypes = AngelType::all();
93        $validation = [];
94        foreach ($angelTypes as $angelType) {
95            $validation['angel_type_' . $angelType->id] = 'optional|int';
96        }
97
98        $data = $this->validate(
99            $request,
100            [
101                'name' => 'required|max:255',
102                'description' => 'optional',
103                'signup_advance_hours' => 'optional|float',
104            ] + $validation
105        );
106
107        if (ShiftType::whereName($data['name'])->where('id', '!=', $shiftType->id)->exists()) {
108            throw new ValidationException((new Validator())->addErrors(['name' => ['validation.name.exists']]));
109        }
110
111        $shiftType->name = $data['name'];
112        $shiftType->description = $data['description'] ?? '';
113        $shiftType->signup_advance_hours = $data['signup_advance_hours'] ?: null;
114
115        $shiftType->save();
116        $shiftType->neededAngelTypes()->delete();
117
118        // Associate angel types with the shift type
119        $angelsInfo = '';
120        foreach ($angelTypes as $angelType) {
121            $count = $data['angel_type_' . $angelType->id];
122            if (!$count) {
123                continue;
124            }
125
126            $neededAngelType = new NeededAngelType();
127
128            $neededAngelType->shiftType()->associate($shiftType);
129            $neededAngelType->angelType()->associate($angelType);
130
131            $neededAngelType->count = $data['angel_type_' . $angelType->id];
132
133            $neededAngelType->save();
134
135            $angelsInfo .= sprintf(', %s: %s', $angelType->name, $count);
136        }
137
138        $this->log->info(
139            'Saved shift type "{name}" ({id}): {description}, {signup_advance_hours}, {angels}',
140            [
141                'id' => $shiftType->id,
142                'name' => $shiftType->name,
143                'description' => $shiftType->description,
144                'signup_advance_hours' => $shiftType->signup_advance_hours,
145                'angels' => $angelsInfo,
146            ]
147        );
148
149        $this->addNotification('shifttype.edit.success');
150
151        return $this->redirect->to('/admin/shifttypes');
152    }
153
154    public function delete(Request $request): Response
155    {
156        $data = $this->validate($request, [
157            'id' => 'required|int',
158            'delete' => 'checked',
159        ]);
160
161        $shiftType = $this->shiftType->findOrFail($data['id']);
162
163        $shifts = $shiftType->shifts;
164        foreach ($shifts as $shift) {
165            event('shift.deleting', ['shift' => $shift]);
166        }
167        $shiftType->delete();
168
169        $this->log->info('Deleted shift type {name} ({id})', ['name' => $shiftType->name, 'id' => $shiftType->id]);
170        $this->addNotification('shifttype.delete.success');
171
172        return $this->redirect->to('/admin/shifttypes');
173    }
174}