NotifierManager   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
wmc 31
eloc 101
c 2
b 0
f 2
dl 0
loc 195
rs 9.92

11 Methods

Rating   Name   Duplication   Size   Complexity  
A createNotification() 0 42 2
A getEventConfig() 0 19 4
A getRegisteredEvents() 0 3 1
A registerEvent() 0 3 1
B sendToChannel() 0 30 6
A schedule() 0 17 4
A getTemplate() 0 11 3
B send() 0 31 7
A getRegisteredChannels() 0 3 1
A sendNow() 0 3 1
A registerChannel() 0 3 1
1
<?php
2
namespace Usamamuneerchaudhary\Notifier\Services;
3
4
use Illuminate\Support\Facades\Cache;
5
use Illuminate\Support\Facades\Log;
6
use Illuminate\Support\Facades\Queue;
7
use Illuminate\Support\Str;
8
use Usamamuneerchaudhary\Notifier\Models\Notification;
9
use Usamamuneerchaudhary\Notifier\Models\NotificationChannel;
10
use Usamamuneerchaudhary\Notifier\Models\NotificationEvent;
11
use Usamamuneerchaudhary\Notifier\Models\NotificationTemplate;
12
use Usamamuneerchaudhary\Notifier\Jobs\SendNotificationJob;
13
14
class NotifierManager
15
{
16
    protected array $channels = [];
17
    protected array $events = [];
18
19
    public function registerChannel(string $type, $handler): void
20
    {
21
        $this->channels[$type] = $handler;
22
    }
23
24
    public function registerEvent(string $key, array $config): void
25
    {
26
        $this->events[$key] = $config;
27
    }
28
29
    public function send($user, string $eventKey, array $data = []): void
30
    {
31
        try {
32
            $eventConfig = $this->getEventConfig($eventKey);
33
            if (!$eventConfig) {
34
                Log::warning("Event configuration not found for: {$eventKey}");
35
                return;
36
            }
37
38
            $preferenceService = app(PreferenceService::class);
39
            $preferences = $preferenceService->getUserPreferences($user, $eventKey);
40
41
            $template = $this->getTemplate($eventConfig['template'] ?? null);
42
            if (!$template) {
0 ignored issues
show
introduced by
$template is of type Usamamuneerchaudhary\Not...ls\NotificationTemplate, thus it always evaluated to true.
Loading history...
43
                Log::warning("Template not found for event: {$eventKey}");
44
                return;
45
            }
46
47
            $preferenceService = app(PreferenceService::class);
48
            foreach ($eventConfig['channels'] ?? [] as $channelType) {
49
                if (!$preferenceService->shouldSendToChannel($user, $channelType, $preferences)) {
50
                    continue;
51
                }
52
53
                $notification = $this->createNotification($user, $template, $channelType, $data, $eventKey);
54
                if (!$notification) {
55
                    continue;
56
                }
57
            }
58
        } catch (\Exception $e) {
59
            Log::error("Failed to send notification for event {$eventKey}: " . $e->getMessage());
60
        }
61
    }
62
63
    public function sendNow($user, string $eventKey, array $data = []): void
64
    {
65
        $this->send($user, $eventKey, $data);
66
    }
67
68
    public function sendToChannel($user, string $eventKey, string $channelType, array $data = []): void
69
    {
70
        try {
71
            $eventConfig = $this->getEventConfig($eventKey);
72
            if (!$eventConfig) {
73
                Log::warning("Event configuration not found for: {$eventKey}");
74
                return;
75
            }
76
77
            $template = $this->getTemplate($eventConfig['template'] ?? null);
78
            if (!$template) {
0 ignored issues
show
introduced by
$template is of type Usamamuneerchaudhary\Not...ls\NotificationTemplate, thus it always evaluated to true.
Loading history...
79
                Log::warning("Template not found for event: {$eventKey}");
80
                return;
81
            }
82
83
            $channel = NotificationChannel::where('type', $channelType)
84
                ->where('is_active', true)
85
                ->first();
86
87
            if (!$channel) {
88
                Log::warning("Channel {$channelType} is not available or active");
89
                return;
90
            }
91
92
            $notification = $this->createNotification($user, $template, $channelType, $data, $eventKey);
93
            if (!$notification) {
94
                return;
95
            }
96
        } catch (\Exception $e) {
97
            Log::error("Failed to send notification to channel {$channelType} for event {$eventKey}: " . $e->getMessage());
98
        }
99
    }
100
101
    public function schedule($user, string $eventKey, \Carbon\Carbon $scheduledAt, array $data = []): void
102
    {
103
        $eventConfig = $this->getEventConfig($eventKey);
104
        $template = $this->getTemplate($eventConfig['template'] ?? null);
105
106
        if (!$template) {
0 ignored issues
show
introduced by
$template is of type Usamamuneerchaudhary\Not...ls\NotificationTemplate, thus it always evaluated to true.
Loading history...
107
            return;
108
        }
109
110
        foreach ($eventConfig['channels'] ?? [] as $channelType) {
111
            $notification = $this->createNotification($user, $template, $channelType, $data, $eventKey);
112
            if (!$notification) {
113
                continue;
114
            }
115
116
            $notification->update(['scheduled_at' => $scheduledAt]);
117
            Queue::later($scheduledAt, new SendNotificationJob($notification->id));
118
        }
119
    }
120
121
    protected function getEventConfig(string $eventKey): ?array
122
    {
123
        if (isset($this->events[$eventKey])) {
124
            return $this->events[$eventKey];
125
        }
126
127
        $event = NotificationEvent::where('key', $eventKey)->first();
128
        if ($event) {
129
            $template = $event->templates()->first();
130
            if ($template) {
131
                $channels = $event->settings['channels'] ?? ['email'];
132
                return [
133
                    'channels' => $channels,
134
                    'template' => $template,
135
                ];
136
            }
137
        }
138
139
        return null;
140
    }
141
142
    protected function getTemplate($template): ?NotificationTemplate
143
    {
144
        if (!$template) {
145
            return null;
146
        }
147
148
        if ($template instanceof NotificationTemplate) {
149
            return $template;
150
        }
151
152
        return NotificationTemplate::where('name', $template)->first();
153
    }
154
155
156
    protected function createNotification($user, NotificationTemplate $template, string $channelType, array $data, string $eventKey): ?Notification
157
    {
158
        $rateLimitingService = app(RateLimitingService::class);
159
        if (!$rateLimitingService->canSend()) {
160
            Log::warning("Notification creation blocked due to rate limit", [
161
                'user_id' => $user->id ?? null,
162
                'event_key' => $eventKey,
163
                'channel' => $channelType,
164
            ]);
165
            return null;
166
        }
167
168
        // Generate tracking token for analytics
169
        $trackingToken = Str::random(32);
170
        $dataWithUser = array_merge($data, ['user' => $user, 'tracking_token' => $trackingToken]);
171
172
        $templateRenderingService = app(TemplateRenderingService::class);
173
        $renderedContent = $templateRenderingService->render($template, $dataWithUser, $channelType);
174
175
        $notificationData = array_merge($data, ['tracking_token' => $trackingToken]);
176
177
        $notification = Notification::create([
178
            'notification_template_id' => $template->id,
179
            'user_id' => $user->id,
180
            'channel' => $channelType,
181
            'subject' => $renderedContent['subject'] ?? '',
182
            'content' => $renderedContent['content'] ?? '',
183
            'data' => $notificationData,
184
            'status' => 'pending',
185
        ]);
186
187
        Cache::put(
188
            "notifier:tracking_token:{$trackingToken}",
189
            $notification->id,
0 ignored issues
show
Bug introduced by
The property id does not seem to exist on Illuminate\Database\Eloq...gHasThroughRelationship.
Loading history...
190
            now()->addDays(30)
191
        );
192
193
        $rateLimitingService->increment();
194
195
        Queue::push(new SendNotificationJob($notification->id));
196
197
        return $notification;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $notification could return the type Illuminate\Database\Eloq...gHasThroughRelationship which is incompatible with the type-hinted return Usamamuneerchaudhary\Not...odels\Notification|null. Consider adding an additional type-check to rule them out.
Loading history...
198
    }
199
200
201
    public function getRegisteredChannels(): array
202
    {
203
        return array_keys($this->channels);
204
    }
205
206
    public function getRegisteredEvents(): array
207
    {
208
        return array_keys($this->events);
209
    }
210
}
211