• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

klinge / sl-webapp / 18124637236

30 Sep 2025 09:04AM UTC coverage: 55.3% (+4.1%) from 51.199%
18124637236

push

github

web-flow
Merge pull request #101 from klinge/dependabot/composer/league/container-tw-5.1

Update league/container requirement from ^4.2 to ^5.1

1132 of 2047 relevant lines covered (55.3%)

2.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.0
/App/Controllers/SeglingController.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Controllers;
6

7
use App\Models\BetalningRepository;
8
use App\Models\SeglingRepository;
9
use App\Models\MedlemRepository;
10
use App\Models\Roll;
11
use App\Utils\Sanitizer;
12
use App\Utils\Session;
13
use App\Utils\View;
14
use App\Application;
15
use PDOException;
16
use Psr\Http\Message\ServerRequestInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Laminas\Diactoros\Response\RedirectResponse;
19
use Laminas\Diactoros\Response\JsonResponse;
20
use Monolog\Logger;
21

22
class SeglingController extends BaseController
23
{
24
    private View $view;
25
    private SeglingRepository $seglingRepo;
26
    private BetalningRepository $betalningRepo;
27
    private MedlemRepository $medlemRepo;
28
    private Roll $roll;
29

30
    public function __construct(
31
        Application $app,
32
        ServerRequestInterface $request,
33
        Logger $logger,
34
        SeglingRepository $seglingRepo,
35
        MedlemRepository $medlemRepo,
36
        BetalningRepository $betalningsRepo,
37
        View $view,
38
        Roll $roll
39
    ) {
40
        parent::__construct($app, $request, $logger);
18✔
41
        $this->seglingRepo = $seglingRepo;
18✔
42
        $this->medlemRepo = $medlemRepo;
18✔
43
        $this->betalningRepo = $betalningsRepo;
18✔
44
        $this->view = $view;
18✔
45
        $this->roll = $roll;
18✔
46
    }
47

48
    public function list(): ResponseInterface
49
    {
50
        $result = $this->seglingRepo->getAllWithDeltagare();
1✔
51

52
        //Put everyting in the data variable that is used by the view
53
        $data = [
1✔
54
            "title" => "Bokningslista",
1✔
55
            "newAction" => $this->app->getRouter()->generate('segling-show-create'),
1✔
56
            "items" => $result
1✔
57
        ];
1✔
58
        return $this->view->render('viewSegling', $data);
1✔
59
    }
60

61
    public function edit(array $params): ResponseInterface
62
    {
63
        $id = (int) $params['id'];
2✔
64
        $formAction = $this->app->getRouter()->generate('segling-save', ['id' => $id]);
2✔
65

66
        $segling = $this->seglingRepo->getById($id);
2✔
67
        if (!$segling) {
2✔
68
            return $this->notFoundResponse();
1✔
69
        }
70
        //Get all deltagare for this segling
71
        $segling->deltagare = $segling->getDeltagare();
1✔
72

73
        //Fetch payment status for deltagare and add to the $deltagare array
74
        $year = (int) substr($segling->start_dat, 0, 4);
1✔
75
        $deltagareWithBetalning = [];
1✔
76

77
        foreach ($segling->deltagare as $deltagare) {
1✔
78
            $hasPayed = $this->betalningRepo->memberHasPayed($deltagare['medlem_id'], $year);
1✔
79
            $deltagare['har_betalt'] = $hasPayed;
1✔
80
            $deltagareWithBetalning[] = $deltagare;
1✔
81
        }
82

83
        //Save the deltagare and betalning info in the $segling object
84
        $segling->deltagare = $deltagareWithBetalning;
1✔
85

86
        //Fetch all available roles
87
        $roller = $this->roll->getAll();
1✔
88

89
        //Fetch lists of persons who has a role to populate select boxes
90
        $allaSkeppare = $this->medlemRepo->getMembersByRollName('Skeppare');
1✔
91
        $allaBatsman = $this->medlemRepo->getMembersByRollName('Båtsman');
1✔
92
        $allaKockar = $this->medlemRepo->getMembersByRollName('Kock');
1✔
93

94
        $data = [
1✔
95
            "title" => "Visa segling",
1✔
96
            "items" => $segling,
1✔
97
            "roles" => $roller,
1✔
98
            "allaSkeppare" => $allaSkeppare,
1✔
99
            "allaBatsman" => $allaBatsman,
1✔
100
            "allaKockar" => $allaKockar,
1✔
101
            "formUrl" => $formAction
1✔
102
        ];
1✔
103
        return $this->view->render('viewSeglingEdit', $data);
1✔
104
    }
105

106
    public function save(array $params): ResponseInterface
107
    {
108
        $id = (int) $params['id'];
2✔
109

110
        $sanitizer = new Sanitizer();
2✔
111
        $rules = [
2✔
112
            'startdat' => ['date', 'Y-m-d'],
2✔
113
            'slutdat' => ['date', 'Y-m-d'],
2✔
114
            'skeppslag' => 'string',
2✔
115
            'kommentar' => 'string',
2✔
116
        ];
2✔
117
        $cleanValues = $sanitizer->sanitize($this->request->getParsedBody(), $rules);
2✔
118

119
        if ($this->seglingRepo->updateSegling($id, $cleanValues)) {
2✔
120
            Session::setFlashMessage('success', 'Segling uppdaterad!');
1✔
121
            $redirectUrl = $this->app->getRouter()->generate('segling-list');
1✔
122
            return new RedirectResponse($redirectUrl);
1✔
123
        } else {
124
            $return = ['success' => false, 'message' => 'Kunde inte uppdatera seglingen. Försök igen.'];
1✔
125
            return new JsonResponse($return);
1✔
126
        }
127
    }
128

129
    public function delete(array $params): ResponseInterface
130
    {
131
        $id = (int) $params['id'];
2✔
132

133
        if ($this->seglingRepo->deleteSegling($id)) {
2✔
134
            Session::setFlashMessage('success', 'Seglingen är nu borttagen!');
1✔
135
            $this->logger->info('Segling was deleted: ' . $id . ' by user: ' . Session::get('user_id'));
1✔
136
        } else {
137
            Session::setFlashMessage('error', 'Kunde inte ta bort seglingen. Försök igen.');
1✔
138
            $this->logger->warning('Failed to delete segling: ' . $id . ' User: ' . Session::get('user_id'));
1✔
139
        }
140
        $redirectUrl = $this->app->getRouter()->generate('segling-list');
2✔
141
        return new RedirectResponse($redirectUrl);
2✔
142
    }
143

144
    public function showCreate(): ResponseInterface
145
    {
146
        $formAction = $this->app->getRouter()->generate('segling-create');
1✔
147
        $data = [
1✔
148
            "title" => "Skapa ny segling",
1✔
149
            "formUrl" => $formAction
1✔
150
        ];
1✔
151
        return $this->view->render('viewSeglingNew', $data);
1✔
152
    }
153

154
    public function create(): ResponseInterface
155
    {
156
        $sanitizer = new Sanitizer();
3✔
157
        $rules = [
3✔
158
            'startdat' => ['date', 'Y-m-d'],
3✔
159
            'slutdat' => ['date', 'Y-m-d'],
3✔
160
            'skeppslag' => 'string',
3✔
161
            'kommentar' => 'string',
3✔
162
        ];
3✔
163
        $cleanValues = $sanitizer->sanitize($this->request->getParsedBody(), $rules);
3✔
164

165
        //Check if requires indata is there, fail otherwise
166
        if (empty($cleanValues['startdat']) || empty($cleanValues['slutdat']) || empty($cleanValues['skeppslag'])) {
3✔
167
            Session::setFlashMessage('error', 'Indata saknades. Kunde inte spara seglingen. Försök igen.');
1✔
168
            $redirectUrl = $this->app->getRouter()->generate('segling-show-create');
1✔
169
            return new RedirectResponse($redirectUrl);
1✔
170
        }
171

172
        $result = $this->seglingRepo->createSegling($cleanValues);
2✔
173

174
        if ($result) {
2✔
175
            Session::setFlashMessage('success', 'Seglingen är nu skapad!');
1✔
176
            $redirectUrl = $this->app->getRouter()->generate('segling-edit', ['id' => $result]);
1✔
177
            return new RedirectResponse($redirectUrl);
1✔
178
        } else {
179
            Session::setFlashMessage('error', 'Kunde inte spara till databas. Försök igen.');
1✔
180
            $redirectUrl = $this->app->getRouter()->generate('segling-show-create');
1✔
181
            return new RedirectResponse($redirectUrl);
1✔
182
        }
183
    }
184

185
    /*
186
    * FUNCTIONS THAT HANDLE Members on a Segling
187
    * called from ajax calls in the client to return json data
188
    */
189
    public function saveMedlem(): ResponseInterface
190
    {
191
        $parsedBody = $this->request->getParsedBody();
4✔
192

193
        if (!isset($parsedBody['segling_id']) || !isset($parsedBody['segling_person'])) {
4✔
194
            return new JsonResponse(['success' => false, 'message' => 'Missing input']);
1✔
195
        }
196

197
        $seglingId = (int) $parsedBody['segling_id'];
3✔
198
        $memberId = (int) $parsedBody['segling_person'];
3✔
199
        $roleId = isset($parsedBody['segling_roll']) ? (int) $parsedBody['segling_roll'] : null;
3✔
200

201
        if ($this->seglingRepo->isMemberOnSegling($seglingId, $memberId)) {
3✔
202
            return new JsonResponse(['success' => false, 'message' => 'Medlemmen är redan tillagd på seglingen.']);
1✔
203
        }
204

205
        try {
206
            if ($this->seglingRepo->addMemberToSegling($seglingId, $memberId, $roleId)) {
2✔
207
                return new JsonResponse(['success' => true, 'message' => 'Medlem tillagd på segling']);
1✔
208
            } else {
209
                return new JsonResponse(['success' => false, 'message' => 'Failed to insert row']);
×
210
            }
211
        } catch (PDOException $e) {
1✔
212
            return new JsonResponse(['success' => false, 'message' => 'PDO error: ' . $e->getMessage()]);
1✔
213
        }
214
    }
215

216
    public function deleteMedlemFromSegling(): ResponseInterface
217
    {
218
        // Handle JSON request body
219
        $contentType = $this->request->getHeaderLine('Content-Type');
3✔
220
        if (strpos($contentType, 'application/json') !== false) {
3✔
221
            $body = $this->request->getBody();
×
222
            if ($body->isSeekable()) {
×
223
                $body->rewind();
×
224
            }
225
            $json = $body->getContents();
×
226
            $data = json_decode($json, true);
×
227
        } else {
228
            $data = $this->request->getParsedBody();
3✔
229
        }
230

231
        $seglingId = $data['segling_id'] ?? null;
3✔
232
        $medlemId = $data['medlem_id'] ?? null;
3✔
233

234

235

236
        if (!$seglingId || !$medlemId) {
3✔
237
            $this->logger->warning("Failed to delete medlem from segling. Invalid data. Medlem: " . $medlemId . " Segling: " . $seglingId);
1✔
238
            return new JsonResponse(['status' => 'fail', 'error' => 'Invalid data']);
1✔
239
        }
240

241
        if ($this->seglingRepo->removeMemberFromSegling((int) $seglingId, (int) $medlemId)) {
2✔
242
            $this->logger->info("Delete medlem from segling. Medlem: " . $medlemId . " Segling: " . $seglingId . " User: " . Session::get('user_id'));
1✔
243
            return new JsonResponse(['status' => 'ok']);
1✔
244
        } else {
245
            $this->logger->warning("Failed to delete medlem from segling. Medlem: " . $medlemId . " Segling: " . $seglingId);
1✔
246
            return new JsonResponse(['status' => 'fail', 'error' => 'Deletion failed']);
1✔
247
        }
248
    }
249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc