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

klinge / sl-webapp / 19065177774

04 Nov 2025 10:12AM UTC coverage: 75.865% (-0.08%) from 75.944%
19065177774

push

github

klinge
Temp reverting back to phpstan lvl 5

1688 of 2225 relevant lines covered (75.87%)

3.95 hits per line

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

74.18
/App/Models/MedlemRepository.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Models;
6

7
use PDO;
8
use Exception;
9
use Psr\Log\LoggerInterface;
10

11
/**
12
 * Repository for managing Medlem (member) entities.
13
 *
14
 * Handles CRUD operations, role management, and member queries.
15
 * Uses repository pattern with pure Medlem data objects.
16
 *
17
 * NOTE: methods named getXXX return Medlem objects, methods named findXXX return raw data arrays.
18
 *
19
 * @package App\Models
20
 */
21
class MedlemRepository extends BaseModel
22
{
23
    public function __construct(PDO $db, LoggerInterface $logger)
24
    {
25
        parent::__construct($db, $logger);
37✔
26
    }
27

28

29
    /**
30
     * Retrieves all members from the database.
31
     *
32
     * Fetches member and creates Medlem objects for each,
33
     * and returns them in an array sorted by last name.
34
     *
35
     * @return array<int, Medlem> An array of Medlem objects
36
     */
37
    public function getAll(): array
38
    {
39
        $medlemmar = [];
×
40

41
        $query = "SELECT id FROM Medlem ORDER BY efternamn ASC";
×
42
        $stmt = $this->conn->prepare($query);
×
43
        $stmt->execute();
×
44
        $members =  $stmt->fetchAll(PDO::FETCH_ASSOC);
×
45

46
        foreach ($members as $member) {
×
47
            try {
48
                $medlem = $this->getById($member['id']);
×
49
                $medlemmar[] = $medlem;
×
50
            } catch (Exception $e) {
×
51
                //Do nothing right now..
52
            }
53
        }
54
        return $medlemmar;
×
55
    }
56

57
    /**
58
     * Retrieves a member by ID.
59
     * Returns a Medlem object or null if not found.
60
     *
61
     * @param int $id The member ID
62
     * @return Medlem|null The Medlem object or null if not found
63
     */
64
    public function getById(int $id): ?Medlem
65
    {
66
        $data = $this->findById($id);
4✔
67
        if (!$data) {
4✔
68
            return null;
×
69
        }
70

71
        $medlem = new Medlem();
4✔
72
        $this->populateMedlem($medlem, $data);
4✔
73
        $medlem->roller = $this->getRolesByMemberId($id);
4✔
74
        return $medlem;
4✔
75
    }
76

77
    /**
78
     * Finds member data by ID.
79
     * Returns raw data array or null if not found.
80
     *
81
     * @param int $id The member ID
82
     * @return array<string, mixed>|null Member data array or null if not found
83
     */
84
    private function findById(int $id): ?array
85
    {
86
        $query = "SELECT * FROM Medlem WHERE id = :id LIMIT 1";
4✔
87
        $stmt = $this->conn->prepare($query);
4✔
88
        $stmt->bindParam(':id', $id);
4✔
89
        $stmt->execute();
4✔
90
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
4✔
91
        return $row ?: null;
4✔
92
    }
93

94
    /**
95
     * Find all Medlemmar in a role by querying Medlem, Roll, and Medlem_Roll tables
96
     * to find members with a specified roll_namn
97
     *
98
     * @param string $rollName The role name to search for
99
     * @return array<int, array<string, mixed>> Array of member data with role info
100
     */
101
    public function findMembersByRollName(string $rollName): array
102
    {
103
        $query = "SELECT m.id,m.fornamn, m.efternamn, r.roll_namn
1✔
104
            FROM  Medlem m
105
            INNER JOIN Medlem_Roll mr ON mr.medlem_id = m.id
106
            INNER JOIN Roll r ON r.id = mr.roll_id
107
            WHERE r.roll_namn = :rollnamn
108
            ORDER BY m.efternamn ASC;";
1✔
109
        $stmt = $this->conn->prepare($query);
1✔
110
        $stmt->bindParam(':rollnamn', $rollName);
1✔
111
        $stmt->execute();
1✔
112
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
1✔
113
    }
114

115
    /**
116
     * Query Medlem, Roll, and Medlem_Roll tables
117
     * to find members with a specified roll_id
118
     *
119
     * @param int $rollId The role ID to search for
120
     * @return array<int, array<string, mixed>> Array of member data with role info
121
     */
122
    public function findMembersByRollId(int $rollId): array
123
    {
124
        $query = "SELECT m.id,m.fornamn, m.efternamn, r.id AS roll_id, r.roll_namn
1✔
125
            FROM  Medlem m
126
            INNER JOIN Medlem_Roll mr ON mr.medlem_id = m.id
127
            INNER JOIN Roll r ON r.id = mr.roll_id
128
            WHERE r.id = :id
129
            ORDER BY m.fornamn ASC;";
1✔
130
        $stmt = $this->conn->prepare($query);
1✔
131
        $stmt->bindParam(':id', $rollId);
1✔
132
        $stmt->execute();
1✔
133
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
1✔
134
    }
135

136
    /**
137
     * Finds member data by email.
138
     *
139
     * @param string $email The email address of the member
140
     * @return array<string, mixed>|false Member data array or false if not found
141
     */
142
    public function findMemberByEmail(string $email): array|false
143
    {
144
        $stmt = $this->conn->prepare("SELECT * FROM medlem WHERE email = :email");
12✔
145
        $stmt->bindParam(':email', $email);
12✔
146
        $stmt->execute();
12✔
147

148
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
12✔
149
        return $result ?: false;
12✔
150
    }
151

152
    /**
153
     * Retrieves member data by email.
154
     *
155
     * @deprecated Use findMemberByEmail() instead
156
     * @param string $email The email address of the member
157
     * @return array<string, mixed>|false Member data array or false if not found
158
     */
159
    public function getMemberByEmail(string $email): array|false
160
    {
161
        return $this->findMemberByEmail($email);
10✔
162
    }
163

164
    /**
165
     * Finds email addresses for active members.
166
     *
167
     * @return array<int, array<string, mixed>> An array of member email data
168
     */
169
    public function findEmailsForActiveMembers(): array
170
    {
171
        $query = "SELECT email FROM medlem WHERE pref_kommunikation = 1";
1✔
172
        $stmt = $this->conn->prepare($query);
1✔
173
        $stmt->execute();
1✔
174
        $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
1✔
175
        //Remove rows with empty emails
176
        return array_filter($result, fn($item) => !empty($item['email']));
1✔
177
    }
178

179
    /**
180
     * Retrieves member email addresses.
181
     *
182
     * @deprecated Use findEmailsForActiveMembers() instead
183
     * @return array<int, array<string, mixed>> An array of member email data
184
     */
185
    public function getEmailForActiveMembers(): array
186
    {
187
        return $this->findEmailsForActiveMembers();
×
188
    }
189

190
    /**
191
     * Creates a new empty Medlem object for data entry.
192
     *
193
     * @return Medlem New Medlem object
194
     */
195
    public function createNew(): Medlem
196
    {
197
        return new Medlem();
1✔
198
    }
199

200
    /**
201
     * Inserts a new member.
202
     *
203
     * @param array<string, mixed> $data Member data
204
     * @return int The new member ID
205
     */
206
    public function insert(array $data): int
207
    {
208
        $params = [
1✔
209
            "fodelsedatum", "fornamn", "efternamn", "email", "gatuadress",
1✔
210
            "postnummer", "postort", "mobil", "telefon", "kommentar",
1✔
211
            "godkant_gdpr", "pref_kommunikation", "isAdmin", "foretag",
1✔
212
            "standig_medlem", "skickat_valkomstbrev"
1✔
213
        ];
1✔
214

215
        $sql = "INSERT INTO Medlem (" . implode(', ', $params) . ") VALUES (" .
1✔
216
               implode(', ', array_map(fn($p) => ":$p", $params)) . ")";
1✔
217

218
        $stmt = $this->conn->prepare($sql);
1✔
219
        $this->bindMemberParams($stmt, $data);
1✔
220
        $stmt->execute();
1✔
221

222
        return (int) $this->conn->lastInsertId();
1✔
223
    }
224

225
    /**
226
     * Updates an existing member.
227
     *
228
     * @param int $id Member ID
229
     * @param array<string, mixed> $data Member data
230
     * @return bool Success status
231
     */
232
    public function update(int $id, array $data): bool
233
    {
234
        $params = [
1✔
235
            "fodelsedatum", "fornamn", "efternamn", "email", "gatuadress",
1✔
236
            "postnummer", "postort", "mobil", "telefon", "kommentar",
1✔
237
            "godkant_gdpr", "pref_kommunikation", "isAdmin", "foretag",
1✔
238
            "standig_medlem", "skickat_valkomstbrev"
1✔
239
        ];
1✔
240

241
        $sql = "UPDATE Medlem SET " . implode(', ', array_map(fn($p) => "$p = :$p", $params)) .
1✔
242
               " WHERE id = :id";
1✔
243

244
        $stmt = $this->conn->prepare($sql);
1✔
245
        $this->bindMemberParams($stmt, $data);
1✔
246
        $stmt->bindParam(':id', $id, PDO::PARAM_INT);
1✔
247
        $stmt->execute();
1✔
248

249
        return true;
1✔
250
    }
251

252
    /**
253
     * Saves a member (create or update).
254
     *
255
     * @param Medlem $medlem The member to save
256
     * @return bool Success status
257
     */
258
    public function save(Medlem $medlem): bool
259
    {
260
        try {
261
            $data = $this->medlemToArray($medlem);
×
262

263
            if (isset($medlem->id) && $medlem->id > 0) {
×
264
                $this->update($medlem->id, $data);
×
265
            } else {
266
                $medlem->id = $this->insert($data);
×
267
            }
268

269
            $this->saveRolesForMember($medlem->id, $medlem->roller);
×
270
            return true;
×
271
        } catch (Exception $e) {
×
272
            $this->logger->error('Failed to save medlem: ' . $e->getMessage());
×
273
            return false;
×
274
        }
275
    }
276

277
    /**
278
     * Deletes a member by ID.
279
     *
280
     * @param int $id Member ID
281
     * @return bool Success status
282
     */
283
    public function deleteById(int $id): bool
284
    {
285
        try {
286
            $this->conn->beginTransaction();
2✔
287

288
            // Remove roles first
289
            $stmt = $this->conn->prepare('DELETE FROM Medlem_Roll WHERE medlem_id = ?');
2✔
290
            $stmt->execute([$id]);
2✔
291

292
            // Remove member
293
            $stmt = $this->conn->prepare('DELETE FROM Medlem WHERE id = ?');
1✔
294
            $stmt->execute([$id]);
1✔
295

296
            $this->conn->commit();
1✔
297
            return true;
1✔
298
        } catch (Exception $e) {
1✔
299
            $this->conn->rollBack();
1✔
300
            $this->logger->error('Failed to delete medlem: ' . $e->getMessage());
1✔
301
            return false;
1✔
302
        }
303
    }
304

305
    /**
306
     * Deletes a member.
307
     *
308
     * @param Medlem $medlem The member to delete
309
     * @return bool Success status
310
     */
311
    public function delete(Medlem $medlem): bool
312
    {
313
        return $this->deleteById($medlem->id);
×
314
    }
315

316
    /**
317
     * Gets roles for a member.
318
     *
319
     * @param int $memberId Member ID
320
     * @return array<int, array<string, mixed>> Array of role data
321
     */
322
    public function getRolesByMemberId(int $memberId): array
323
    {
324
        $query = "SELECT mr.roll_id, r.roll_namn 
5✔
325
                  FROM Medlem_Roll mr
326
                  INNER JOIN Roll r ON mr.roll_id = r.id
327
                  WHERE mr.medlem_id = :id";
5✔
328

329
        $stmt = $this->conn->prepare($query);
5✔
330
        $stmt->bindParam(':id', $memberId);
5✔
331
        $stmt->execute();
5✔
332
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
5✔
333
    }
334

335
    /**
336
     * Saves roles for a member.
337
     *
338
     * @param int $memberId Member ID
339
     * @param array<int, array<string, mixed>> $roles Array of role data
340
     * @return void
341
     */
342
    public function saveRolesForMember(int $memberId, array $roles): void
343
    {
344
        try {
345
            $this->conn->beginTransaction();
1✔
346

347
            // Get current roles
348
            $stmt = $this->conn->prepare("SELECT roll_id FROM Medlem_Roll WHERE medlem_id = :medlem_id");
1✔
349
            $stmt->execute(['medlem_id' => $memberId]);
1✔
350
            $currentRoles = $stmt->fetchAll(PDO::FETCH_COLUMN);
1✔
351

352
            $newRoles = array_map(fn($role) => (int) $role['roll_id'], $roles);
1✔
353

354
            // Add new roles
355
            $rolesToAdd = array_diff($newRoles, $currentRoles);
1✔
356
            if (!empty($rolesToAdd)) {
1✔
357
                $stmt = $this->conn->prepare("INSERT INTO Medlem_Roll (medlem_id, roll_id) VALUES (:medlem_id, :roll_id)");
1✔
358
                foreach ($rolesToAdd as $rollId) {
1✔
359
                    $stmt->execute(['medlem_id' => $memberId, 'roll_id' => $rollId]);
1✔
360
                }
361
            }
362

363
            // Remove old roles
364
            $rolesToRemove = array_diff($currentRoles, $newRoles);
1✔
365
            if (!empty($rolesToRemove)) {
1✔
366
                $stmt = $this->conn->prepare("DELETE FROM Medlem_Roll WHERE medlem_id = :medlem_id AND roll_id = :roll_id");
×
367
                foreach ($rolesToRemove as $rollId) {
×
368
                    $stmt->execute(['medlem_id' => $memberId, 'roll_id' => $rollId]);
×
369
                }
370
            }
371

372
            $this->conn->commit();
1✔
373
        } catch (Exception $e) {
×
374
            $this->conn->rollBack();
×
375
            $this->logger->error("Error updating roles for medlem ID $memberId: " . $e->getMessage());
×
376
            throw $e;
×
377
        }
378
    }
379

380
    /**
381
     * Gets seglingar for a member.
382
     *
383
     * @param int $memberId Member ID
384
     * @return array<int, array<string, mixed>> Array of segling data
385
     */
386
    public function getSeglingarByMemberId(int $memberId): array
387
    {
388
        $query = 'SELECT smr.medlem_id, s.id as segling_id, r.roll_namn, s.skeppslag, s.startdatum
1✔
389
            FROM Segling_Medlem_Roll smr
390
            INNER JOIN Segling s ON s.id = smr.segling_id
391
            LEFT JOIN Roll r ON r.id = smr.roll_id
392
            WHERE smr.medlem_id = :id
393
            ORDER BY s.startdatum DESC
394
            LIMIT 10';
1✔
395

396
        $stmt = $this->conn->prepare($query);
1✔
397
        $stmt->bindParam(':id', $memberId);
1✔
398
        $stmt->execute();
1✔
399
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
1✔
400
    }
401

402
    /**
403
     * Binds member parameters to prepared statement.
404
     *
405
     * @param \PDOStatement $stmt The prepared statement
406
     * @param array<string, mixed> $data Member data
407
     * @return void
408
     */
409
    private function bindMemberParams($stmt, array $data): void
410
    {
411
        $stmt->bindParam(':fodelsedatum', $data['fodelsedatum'], PDO::PARAM_STR);
2✔
412
        $stmt->bindParam(':fornamn', $data['fornamn'], PDO::PARAM_STR);
2✔
413
        $stmt->bindParam(':efternamn', $data['efternamn'], PDO::PARAM_STR);
2✔
414
        $email = $data['email'] ?: null;
2✔
415
        $stmt->bindParam(':email', $email, PDO::PARAM_STR);
2✔
416
        $stmt->bindParam(':gatuadress', $data['adress'], PDO::PARAM_STR);
2✔
417
        $stmt->bindParam(':postnummer', $data['postnummer'], PDO::PARAM_STR);
2✔
418
        $stmt->bindParam(':postort', $data['postort'], PDO::PARAM_STR);
2✔
419
        $stmt->bindParam(':mobil', $data['mobil'], PDO::PARAM_STR);
2✔
420
        $stmt->bindParam(':telefon', $data['telefon'], PDO::PARAM_STR);
2✔
421
        $stmt->bindParam(':kommentar', $data['kommentar'], PDO::PARAM_STR);
2✔
422
        $stmt->bindParam(':godkant_gdpr', $data['godkant_gdpr'], PDO::PARAM_BOOL);
2✔
423
        $stmt->bindParam(':pref_kommunikation', $data['pref_kommunikation'], PDO::PARAM_BOOL);
2✔
424
        $stmt->bindParam(':isAdmin', $data['isAdmin'], PDO::PARAM_BOOL);
2✔
425
        $stmt->bindParam(':foretag', $data['foretag'], PDO::PARAM_BOOL);
2✔
426
        $stmt->bindParam(':standig_medlem', $data['standig_medlem'], PDO::PARAM_BOOL);
2✔
427
        $stmt->bindParam(':skickat_valkomstbrev', $data['skickat_valkomstbrev'], PDO::PARAM_BOOL);
2✔
428
    }
429

430
    /**
431
     * Populates a Medlem object with data from database row.
432
     *
433
     * @param Medlem $medlem The member object to populate
434
     * @param array<string, mixed> $data Database row data
435
     * @return void
436
     */
437
    private function populateMedlem(Medlem $medlem, array $data): void
438
    {
439
        $medlem->id = (int) $data['id'];
4✔
440
        $medlem->fodelsedatum = $data['fodelsedatum'] ?? null;
4✔
441
        $medlem->fornamn = $data['fornamn'] ?? null;
4✔
442
        $medlem->efternamn = $data['efternamn'];
4✔
443
        $medlem->email = $data['email'] ?? null;
4✔
444
        $medlem->mobil = $data['mobil'] ?? null;
4✔
445
        $medlem->telefon = $data['telefon'] ?? null;
4✔
446
        $medlem->adress = $data['gatuadress'] ?? null;
4✔
447
        $medlem->postnummer = $data['postnummer'] ?? null;
4✔
448
        $medlem->postort = $data['postort'] ?? null;
4✔
449
        $medlem->kommentar = $data['kommentar'] ?? null;
4✔
450
        $medlem->godkant_gdpr = (bool) $data['godkant_gdpr'];
4✔
451
        $medlem->pref_kommunikation = (bool) $data['pref_kommunikation'];
4✔
452
        $medlem->foretag = (bool) $data['foretag'];
4✔
453
        $medlem->standig_medlem = (bool) $data['standig_medlem'];
4✔
454
        $medlem->skickat_valkomstbrev = (bool) $data['skickat_valkomstbrev'];
4✔
455
        $medlem->isAdmin = (bool) $data['isAdmin'];
4✔
456
        $medlem->password = $data['password'] ?? null;
4✔
457
        $medlem->created_at = $data['created_at'];
4✔
458
        $medlem->updated_at = $data['updated_at'];
4✔
459
    }
460

461
    /**
462
     * Converts a Medlem object to array for database operations.
463
     *
464
     * @param Medlem $medlem The member object
465
     * @return array<string, mixed> Member data as array
466
     */
467
    private function medlemToArray(Medlem $medlem): array
468
    {
469
        return [
×
470
            'fodelsedatum' => $medlem->fodelsedatum,
×
471
            'fornamn' => $medlem->fornamn,
×
472
            'efternamn' => $medlem->efternamn,
×
473
            'email' => $medlem->email,
×
474
            'adress' => $medlem->adress,
×
475
            'postnummer' => $medlem->postnummer,
×
476
            'postort' => $medlem->postort,
×
477
            'mobil' => $medlem->mobil,
×
478
            'telefon' => $medlem->telefon,
×
479
            'kommentar' => $medlem->kommentar,
×
480
            'godkant_gdpr' => $medlem->godkant_gdpr,
×
481
            'pref_kommunikation' => $medlem->pref_kommunikation,
×
482
            'isAdmin' => $medlem->isAdmin,
×
483
            'foretag' => $medlem->foretag,
×
484
            'standig_medlem' => $medlem->standig_medlem,
×
485
            'skickat_valkomstbrev' => $medlem->skickat_valkomstbrev
×
486
        ];
×
487
    }
488
}
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