Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
NotificationPreferenceRepository
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
4 / 4
6
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
 getByUserId
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 upsert
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 getEnabledRecipientsForAdminType
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace App\Domain\Notification\Repository;
6
7use App\Domain\Notification\Data\NotificationPreference;
8use App\Domain\Notification\Data\NotificationType;
9use PDO;
10
11/**
12 * PDO-backed access to the notification_preferences table.
13 *
14 * Recipient resolution for a given notification type goes through
15 * {@see getEnabledRecipientsForAdminType()} — which joins users to filter
16 * by role. Investor-targeted types will get their own resolver method
17 * once those types are added.
18 */
19final class NotificationPreferenceRepository implements NotificationPreferenceRepositoryInterface
20{
21    public function __construct(
22        private readonly PDO $pdo,
23    ) {}
24
25    /**
26     * @param int $userId
27     * @return list<NotificationPreference>
28     */
29    public function getByUserId(int $userId): array
30    {
31        $sql = '
32            SELECT
33                preference_id   AS "preferenceId",
34                user_id         AS "userId",
35                notification_type AS "notificationType",
36                channel,
37                enabled,
38                created_at      AS "createdAt",
39                updated_at      AS "updatedAt"
40            FROM notification_preferences
41            WHERE user_id = :user_id
42            ORDER BY notification_type
43        ';
44
45        $stmt = $this->pdo->prepare($sql);
46        $stmt->execute(['user_id' => $userId]);
47
48        $rows = [];
49        /** @var array<string, mixed> $row */
50        foreach ($stmt->fetchAll() as $row) {
51            $rows[] = new NotificationPreference($row);
52        }
53
54        return $rows;
55    }
56
57    /**
58     * Insert or update a single preference. UPSERT keyed on the unique
59     * (user_id, notification_type, channel) combination.
60     * @param int $userId
61     * @param string $notificationType
62     * @param string $channel
63     * @param bool $enabled
64     */
65    public function upsert(int $userId, string $notificationType, string $channel, bool $enabled): void
66    {
67        $sql = '
68            INSERT INTO notification_preferences (user_id, notification_type, channel, enabled)
69            VALUES (:user_id, :notification_type, :channel, :enabled)
70            ON CONFLICT (user_id, notification_type, channel)
71            DO UPDATE SET
72                enabled = EXCLUDED.enabled,
73                updated_at = CURRENT_TIMESTAMP
74        ';
75
76        $stmt = $this->pdo->prepare($sql);
77        $stmt->execute([
78            'user_id' => $userId,
79            'notification_type' => $notificationType,
80            'channel' => $channel,
81            'enabled' => $enabled ? 't' : 'f',
82        ]);
83    }
84
85    /**
86     * Return the email addresses of admin / super_admin users who have
87     * opted into the given admin notification type.
88     *
89     * @param string $notificationType
90     * @return list<string>
91     */
92    public function getEnabledRecipientsForAdminType(string $notificationType): array
93    {
94        $sql = '
95            SELECT u.email
96            FROM notification_preferences np
97            JOIN users u ON u.user_id = np.user_id
98            WHERE np.notification_type = :notification_type
99              AND np.channel = :channel
100              AND np.enabled = TRUE
101              AND u.is_active = TRUE
102              AND u.role IN (\'admin\', \'super_admin\')
103        ';
104
105        $stmt = $this->pdo->prepare($sql);
106        $stmt->execute([
107            'notification_type' => $notificationType,
108            'channel' => NotificationType::CHANNEL_EMAIL,
109        ]);
110
111        /** @var list<string> $emails */
112        $emails = $stmt->fetchAll(PDO::FETCH_COLUMN);
113
114        return $emails;
115    }
116}