CMS Framework - v2.2.2
Notifications — Hooks and Events
The Notifications module provides filter and action hooks that allow you to extend and customize behavior.
Available Hooks
Filters
| Hook Name | Description | Parameters | Return Type |
|---|---|---|---|
ap.notifications.registeredNotifications |
Filter the array of registered notifications | $notifications (array) |
array |
Actions
| Hook Name | Description | Parameters |
|---|---|---|
ap.notifications.sendNotification |
Fires after a notification has been sent | $notification (Notification), $userIds (array), $key (string) |
ap.notifications.readNotification |
Fires after a notification has been marked as read | $notificationId (int), $userId (int) |
ap.notifications.dismissNotification |
Fires after a notification has been dismissed | $notificationId (int), $userId (int) |
Filter Hooks
ap.notifications.registeredNotifications
This filter allows you to register notifications or modify existing registrations.
Basic Usage
use function addFilter;
use ArtisanPackUI\CMSFramework\Modules\Notifications\Enums\NotificationType;
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
$notifications['custom.event'] = [
'title' => 'Custom Event',
'content' => 'A custom event occurred in your application.',
'type' => NotificationType::Info,
'send_email' => false,
'metadata' => ['source' => 'custom_module']
];
return $notifications;
});
Modify Existing Notifications
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
// Override default content for an existing notification
if (isset($notifications['post.published'])) {
$notifications['post.published']['content'] = 'Check out the latest post!';
}
return $notifications;
}, 20); // Higher priority runs later
Bulk Registration
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
$customNotifications = [
'order.created' => [
'title' => 'Order Created',
'content' => 'Your order has been created successfully.',
'type' => NotificationType::Success,
'send_email' => true,
'metadata' => ['module' => 'ecommerce']
],
'order.shipped' => [
'title' => 'Order Shipped',
'content' => 'Your order has been shipped.',
'type' => NotificationType::Info,
'send_email' => true,
'metadata' => ['module' => 'ecommerce']
],
'order.delivered' => [
'title' => 'Order Delivered',
'content' => 'Your order has been delivered.',
'type' => NotificationType::Success,
'send_email' => true,
'metadata' => ['module' => 'ecommerce']
],
];
return array_merge($notifications, $customNotifications);
});
Conditional Registration
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
// Only register in production
if (app()->environment('production')) {
$notifications['system.critical_error'] = [
'title' => 'Critical System Error',
'content' => 'A critical error occurred.',
'type' => NotificationType::Error,
'send_email' => true,
'metadata' => ['priority' => 'critical']
];
}
return $notifications;
});
Remove Notifications
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
// Remove a notification type
unset($notifications['unwanted.notification']);
return $notifications;
});
Action Hooks
ap.notifications.sendNotification
Fires immediately after a notification has been sent to users.
Parameters
$notification(Notification) — The created notification instance$userIds(array) — Array of user IDs that received the notification$key(string) — The notification key
Basic Usage
use function addAction;
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// Log the notification
logger()->info("Notification sent", [
'key' => $key,
'id' => $notification->id,
'recipients' => count($userIds)
]);
});
Send to External Services
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// Send to Slack for certain notification types
if ($notification->type === NotificationType::Error) {
app(SlackNotifier::class)->send(
"Error notification sent: {$notification->title}",
$userIds
);
}
});
Track Analytics
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// Track in analytics
foreach ($userIds as $userId) {
Analytics::track($userId, 'notification_received', [
'notification_key' => $key,
'notification_type' => $notification->type->value,
'has_email' => $notification->send_email
]);
}
});
Broadcast Real-Time Updates
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// Broadcast to frontend via Laravel Echo
foreach ($userIds as $userId) {
broadcast(new NotificationReceived($notification, $userId))->toOthers();
}
});
Create Activity Log
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// Log to activity table
foreach ($userIds as $userId) {
Activity::create([
'user_id' => $userId,
'type' => 'notification_received',
'description' => $notification->title,
'metadata' => [
'notification_id' => $notification->id,
'key' => $key
]
]);
}
});
ap.notifications.readNotification
Fires after a notification has been marked as read by a user.
Parameters
$notificationId(int) — The notification ID$userId(int) — The user ID who marked it as read
Basic Usage
addAction('ap.notifications.readNotification', function ($notificationId, $userId) {
logger()->info("Notification marked as read", [
'notification_id' => $notificationId,
'user_id' => $userId
]);
});
Track Engagement
addAction('ap.notifications.readNotification', function ($notificationId, $userId) {
$notification = Notification::find($notificationId);
Analytics::track($userId, 'notification_read', [
'notification_key' => $notification->metadata['key'] ?? 'unknown',
'notification_type' => $notification->type->value,
'time_to_read' => now()->diffInMinutes($notification->created_at)
]);
});
Update User Engagement Score
addAction('ap.notifications.readNotification', function ($notificationId, $userId) {
$user = User::find($userId);
$user->increment('engagement_score');
});
ap.notifications.dismissNotification
Fires after a notification has been dismissed by a user.
Parameters
$notificationId(int) — The notification ID$userId(int) — The user ID who dismissed it
Basic Usage
addAction('ap.notifications.dismissNotification', function ($notificationId, $userId) {
logger()->info("Notification dismissed", [
'notification_id' => $notificationId,
'user_id' => $userId
]);
});
Track Dismissal Patterns
addAction('ap.notifications.dismissNotification', function ($notificationId, $userId) {
$notification = Notification::find($notificationId);
// Track which notification types are frequently dismissed
Analytics::track($userId, 'notification_dismissed', [
'notification_type' => $notification->type->value,
'was_read' => $notification->users()
->where('user_id', $userId)
->first()->pivot->is_read
]);
});
Suggest Preference Changes
addAction('ap.notifications.dismissNotification', function ($notificationId, $userId) {
$notification = Notification::find($notificationId);
$key = $notification->metadata['key'] ?? null;
if (!$key) return;
// Track dismissals per notification type
$dismissals = Cache::increment("user.{$userId}.dismissals.{$key}");
// After 5 dismissals, suggest disabling this type
if ($dismissals >= 5) {
apSendNotificationToCurrentUser('suggestion.disable_notifications', [
'content' => "We noticed you often dismiss '{$key}' notifications. Would you like to disable them?",
'metadata' => ['suggested_key' => $key]
]);
Cache::forget("user.{$userId}.dismissals.{$key}");
}
});
Practical Examples
Multi-Channel Notifications
Send notifications to multiple channels (database, email, SMS, push):
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// SMS for critical notifications
if ($notification->type === NotificationType::Error) {
foreach ($userIds as $userId) {
$user = User::find($userId);
if ($user->phone) {
SMS::send($user->phone, $notification->content);
}
}
}
// Push notifications for mobile users
foreach ($userIds as $userId) {
$user = User::find($userId);
if ($user->push_token) {
PushNotification::send($user->push_token, [
'title' => $notification->title,
'body' => $notification->content
]);
}
}
});
Digest Notifications
Batch notifications into daily/weekly digests:
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// For low-priority notifications, add to digest instead of sending immediately
if (in_array($key, ['post.liked', 'post.comment'])) {
foreach ($userIds as $userId) {
DigestNotification::create([
'user_id' => $userId,
'notification_id' => $notification->id,
'scheduled_for' => now()->endOfDay() // Send at end of day
]);
}
// Prevent immediate email
$notification->update(['send_email' => false]);
}
});
Notification Rate Limiting
Prevent notification spam:
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
return $notifications;
});
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
foreach ($userIds as $userId) {
$rateLimitKey = "notification_limit.{$userId}.{$key}";
// Allow max 5 of this notification type per hour
if (Cache::get($rateLimitKey, 0) >= 5) {
// Detach this user from the notification
$notification->users()->detach($userId);
logger()->warning("Rate limit exceeded", [
'user_id' => $userId,
'key' => $key
]);
continue;
}
Cache::increment($rateLimitKey);
Cache::put($rateLimitKey, Cache::get($rateLimitKey), now()->addHour());
}
});
Notification Templates
Override notification content with templates:
addFilter('ap.notifications.registeredNotifications', function ($notifications) {
foreach ($notifications as $key => &$notification) {
// Load from template files if they exist
$templatePath = resource_path("notifications/{$key}.blade.php");
if (file_exists($templatePath)) {
$notification['content'] = view("notifications.{$key}")->render();
}
}
return $notifications;
});
User Mention Notifications
Automatically detect and notify mentioned users:
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
// Detect @mentions in content
preg_match_all('/@(\w+)/', $notification->content, $mentions);
if (!empty($mentions[1])) {
$mentionedUsers = User::whereIn('username', $mentions[1])->pluck('id')->toArray();
// Send mention notification
apSendNotification('user.mentioned', $mentionedUsers, [
'content' => "You were mentioned in: {$notification->title}",
'metadata' => [
'source_notification_id' => $notification->id
]
]);
}
});
Conditional Email Sending
Override email sending based on user settings or time of day:
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
if ($notification->send_email) {
$currentHour = now()->hour;
// Don't send emails between 10 PM and 8 AM (quiet hours)
if ($currentHour >= 22 || $currentHour < 8) {
// Reschedule for 8 AM
SendNotificationEmail::dispatch($notification, $userIds)
->delay(now()->setTime(8, 0));
// Mark as not sent immediately
$notification->update(['send_email' => false]);
}
}
});
Best Practices
Use Appropriate Hook Types
- Filters — Modify data (registered notifications)
- Actions — Execute side effects (logging, analytics, external services)
Set Proper Priorities
// Run early (priority 1-5)
addAction('ap.notifications.sendNotification', 'earlyCallback', 1);
// Run default (priority 10)
addAction('ap.notifications.sendNotification', 'normalCallback');
// Run late (priority 20+)
addAction('ap.notifications.sendNotification', 'lateCallback', 20);
Handle Errors Gracefully
addAction('ap.notifications.sendNotification', function ($notification, $userIds, $key) {
try {
// Your logic
externalService()->notify($userIds);
} catch (\Exception $e) {
logger()->error("Failed to send to external service", [
'error' => $e->getMessage(),
'notification_id' => $notification->id
]);
// Don't throw - allow other hooks to run
}
});
Document Custom Hooks
When adding hooks to your own modules, document them:
/**
* Filters the notification content before sending.
*
* @hook custom_module.notification.content
*
* @param string $content The notification content.
* @param Notification $notification The notification instance.
*
* @return string Filtered content.
*/
$content = applyFilters('custom_module.notification.content', $content, $notification);
Next Steps
- Review Database and Migrations for schema details
- Explore the API Reference for all available methods
- Learn about Registering Notifications and Sending Notifications