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