Notification
This comprehensive guide covers Glueful's notification system, including multi-channel delivery, email notifications, template management, and advanced features for enterprise-grade notification management.
Table of Contents
- Overview
- Architecture
- Core Components
- Email Notification Channel
- Multi-Channel Support
- API Endpoints
- Configuration
- Usage Examples
- Template System
- Advanced Features
- Error Handling
- Best Practices
Overview
The Glueful Notification System, introduced in v0.19.0, provides a comprehensive solution for managing user notifications across multiple channels. The system is designed for enterprise-grade applications requiring reliable, scalable, and flexible notification delivery.
Key Features
- Multi-Channel Delivery: In-app, email, and extensible channel architecture
- Template-Based Notifications: Reusable templates with variable substitution
- Email Integration: Full-featured email notification system with multiple providers
- Delivery Tracking: Read/unread status, delivery confirmation, and retry mechanisms
- Queue Integration: Asynchronous notification processing with queue support
- Event-Driven Architecture: Comprehensive event system for notification lifecycle
- Advanced Configuration: Flexible configuration for different environments
- Performance Optimization: Caching, batching, and efficient database operations
Architecture
The notification system follows a modular, event-driven architecture:
graph TD
A[Notification Service] --> B[Event Dispatcher]
B --> C[Email Notification Channel]
B --> D[Database Notification Channel]
B --> E[Custom Channels...]
C --> F[Email Providers]
F --> G[SMTP]
F --> H[Sendgrid API]
F --> I[Mailgun API]
F --> J[Custom Providers]
A --> K[Notification Repository]
A --> L[Template Manager]
A --> M[Queue System]
Core Components
- NotificationService: Central service for notification management
- NotificationRepository: Data access layer for notifications
- Event Dispatcher: Handles notification events and channel routing
- Template Manager: Manages notification templates and rendering
- Channel System: Pluggable delivery channels (email, database, etc.)
- Queue Integration: Asynchronous processing and retry mechanisms
Core Components
Notification Service
The NotificationService
is the primary interface for notification management:
use Glueful\Notifications\Services\NotificationService;
$notificationService = container()->get(NotificationService::class);
// Send notification
$result = $notificationService->send(
'user_welcome', // type
$notifiableUser, // recipient (implements Notifiable)
'Welcome to our platform!', // subject
['username' => $user->name], // data
['channels' => ['email', 'database']] // options
);
// Template-based notifications
$result = $notificationService->sendWithTemplate(
'password_reset',
$notifiableUser,
'password-reset-template',
['reset_url' => $resetUrl],
['channels' => ['email']]
);
// Scheduled notifications
$notificationService->create(
'subscription_reminder',
$user,
'Your subscription expires soon',
['expiry_date' => $expiryDate],
['channels' => ['email'], 'schedule' => new DateTime('+7 days')]
);
Notification Repository
Database operations and querying:
use Glueful\Repository\NotificationRepository;
$repository = container()->get(NotificationRepository::class);
// Get user notifications
// Fetch notifications for a notifiable (type + id)
$notifications = $repository->findForNotifiable('user', $userUuid);
// Count unread notifications
$unread = $repository->countForNotifiable('user', $userUuid, true);
// Query with filters
$recent = $repository->findForNotifiable('user', $userUuid, true, 10, 0, [
'type' => 'order_update',
]);
// Mark notifications as read
// Mark single notification as read via service (after loading model)
// $notification = $repository->findByUuid($notificationUuid);
// $notificationService->markAsRead($notification);
// Mark all as read for a notifiable using service
// $notificationService->markAllAsRead($user);
Event System
The notification system dispatches events throughout the notification lifecycle:
// Available Events
use Glueful\Notifications\Events\NotificationSent;
use Glueful\Notifications\Events\NotificationFailed;
use Glueful\Notifications\Events\NotificationRead;
// Event Listeners
class NotificationEventListener
{
public function onNotificationSent(NotificationSent $event): void
{
$notification = $event->getNotification();
// Track successful delivery
}
public function onNotificationFailed(NotificationFailed $event): void
{
$notification = $event->getNotification();
$reason = $event->getReason();
// Handle delivery failure
}
public function onNotificationRead(NotificationRead $event): void
{
$notification = $event->getNotification();
// Track read status
}
}
Email Notification Channel
The email notification channel provides comprehensive email delivery capabilities.
Email Providers
Multiple email providers are supported:
SMTP Configuration
// config/mail.php
return [
'providers' => [
'smtp' => [
'host' => 'smtp.example.com',
'port' => 587,
'encryption' => 'tls',
'username' => 'your-username',
'password' => 'your-password',
'timeout' => 30,
],
],
'default' => 'smtp',
];
Sendgrid API
'sendgrid' => [
'api_key' => 'your-sendgrid-api-key',
'endpoint' => 'https://api.sendgrid.com/v3/mail/send',
'from' => [
'address' => '[email protected]',
'name' => 'Your App'
],
],
Mailgun API
'mailgun' => [
'api_key' => 'your-mailgun-api-key',
'domain' => 'mail.yourdomain.com',
'endpoint' => 'https://api.mailgun.net/v3/',
],
Email Features
Basic Email Sending
// Simple email notification
$emailNotification = Extensions::get('email_notification');
$emailNotification->send([
'to' => '[email protected]',
'subject' => 'Welcome to Our Platform',
'template' => 'welcome',
'variables' => [
'user_name' => 'John Doe',
'verification_link' => 'https://example.com/verify/token123'
]
]);
Advanced Email Options
// Email with attachments and advanced options
$emailNotification->send([
'to' => [
['email' => '[email protected]', 'name' => 'Recipient One'],
['email' => '[email protected]', 'name' => 'Recipient Two']
],
'cc' => ['[email protected]'],
'bcc' => ['[email protected]'],
'subject' => 'Your Invoice #123',
'template' => 'invoice',
'variables' => [
'invoice_number' => '123',
'amount' => '$99.99',
'due_date' => '2025-05-15'
],
'attachments' => [
[
'path' => '/path/to/invoice.pdf',
'name' => 'Invoice-123.pdf',
'mime' => 'application/pdf'
]
],
'provider' => 'sendgrid', // Override default provider
'priority' => 'high',
'track_opens' => true,
'track_clicks' => true
]);
Batch Email Processing
// Send multiple emails efficiently
$emails = [
[
'to' => '[email protected]',
'template' => 'welcome',
'variables' => ['name' => 'User 1']
],
[
'to' => '[email protected]',
'template' => 'welcome',
'variables' => ['name' => 'User 2']
]
];
// Loop or implement an app-level helper to batch
foreach ($emails as $email) {
$emailNotification->send($email);
}
Multi-Channel Support
Channel Priority and Selection
The notification system supports intelligent channel selection:
// Channel selection priority:
// 1. Explicit channels in options
// 2. User preferences
// 3. Default configuration
$notificationService->send(
'order_shipped',
$user,
'Your order has shipped',
['tracking_number' => 'ABC123'],
[
'channels' => ['email', 'database'], // Explicit channels
'priority' => 'high' // High priority notifications
]
);
Channel Configuration
// config/notifications.php
return [
'channels' => [
'email' => [
'enabled' => true,
'driver' => 'smtp',
'queue' => 'email-notifications',
'retry_attempts' => 3,
'retry_delay' => 300, // seconds
],
'database' => [
'enabled' => true,
'table' => 'notifications',
],
'slack' => [
'enabled' => false,
'webhook_url' => 'https://hooks.slack.com/...',
],
],
'default_channels' => ['database'],
'user_preferences' => [
'enabled' => true,
'table' => 'user_notification_preferences',
],
];
Custom Channels
Create custom notification channels:
use Glueful\Notifications\Contracts\NotificationChannel;
use Glueful\Notifications\Contracts\Notifiable;
class SlackChannel implements NotificationChannel
{
public function getChannelName(): string { return 'slack'; }
public function isAvailable(): bool { return (bool) config('notifications.channels.slack.enabled'); }
public function getConfig(): array { return config('notifications.channels.slack') ?? []; }
public function format(array $data, Notifiable $notifiable): array
{
return [
'text' => $data['subject'] ?? '',
'attachments' => [
[
'color' => 'good',
'fields' => [
[
'title' => 'Message',
'value' => $data['content'] ?? '',
'short' => false
]
]
]
]
];
}
public function send(Notifiable $notifiable, array $data): bool
{
$webhookUrl = config('notifications.channels.slack.webhook_url');
$response = http_client()->post($webhookUrl, ['json' => $data]);
return $response->getStatusCode() === 200;
}
}
API Endpoints
REST API
Method | Endpoint | Description |
---|---|---|
GET | /api/notifications | Get all notifications for authenticated user |
GET | /api/notifications/unread | Get unread notifications |
GET | /api/notifications/{uuid} | Get a specific notification |
POST | /api/notifications/mark-read/{uuid} | Mark a notification as read |
POST | /api/notifications/mark-all-read | Mark all notifications as read |
DELETE | /api/notifications/{uuid} | Delete a notification |
GET | /api/notifications/preferences | Get user notification preferences |
PUT | /api/notifications/preferences | Update user notification preferences |
API Usage Examples
// Get notifications with pagination
GET /api/notifications?page=1&limit=10&type=order_update
// Response
{
"success": true,
"data": {
"notifications": [...],
"pagination": {
"current_page": 1,
"per_page": 10,
"total": 45,
"total_pages": 5
},
"unread_count": 12
}
}
// Mark notification as read
POST /api/notifications/mark-read/uuid-123
{
"success": true,
"message": "Notification marked as read"
}
// Update preferences
PUT /api/notifications/preferences
{
"email_notifications": true,
"types": {
"order_updates": ["email", "database"],
"promotions": ["database"],
"security_alerts": ["email"]
}
}
Configuration
Main Configuration
// config/notifications.php
return [
'channels' => [
'email' => [
'enabled' => true,
'driver' => 'smtp',
'from' => [
'address' => '[email protected]',
'name' => 'Notification System'
],
'template_path' => 'storage/templates/email',
'queue' => 'email-notifications',
'retry_attempts' => 3,
'retry_delay' => 300,
],
'database' => [
'enabled' => true,
'table' => 'notifications',
'cleanup_after' => 90, // days
],
],
'default_channels' => ['database'],
'templates' => [
'cache_enabled' => true,
'cache_ttl' => 3600,
'fallback_language' => 'en',
],
'queue' => [
'enabled' => true,
'connection' => 'redis',
'queue' => 'notifications',
'batch_size' => 50,
],
'retry' => [
'max_attempts' => 3,
'delay' => 300, // seconds
'exponential_backoff' => true,
],
'user_preferences' => [
'enabled' => true,
'defaults' => [
'email_notifications' => true,
'database_notifications' => true,
],
],
];
Email Extension Configuration
// Extension configuration in extensions.php
'extensions' => [
'email_notification' => [
'enabled' => true,
'config' => [
'default_provider' => 'smtp',
'template_path' => 'storage/templates/email',
'cache' => [
'enabled' => true,
'driver' => 'redis',
'ttl' => 3600
],
'tracking' => [
'opens' => true,
'clicks' => true,
'bounces' => true,
],
]
]
]
Usage Examples
Basic Notification Sending
use Glueful\Notifications\Services\NotificationService;
class OrderController
{
private NotificationService $notificationService;
public function shipOrder(string $orderId): Response
{
$order = $this->orderService->ship($orderId);
// Send shipping notification
$this->notificationService->send(
'order_shipped',
$order->getUser(),
'Your order has shipped!',
[
'order_id' => $order->getId(),
'tracking_number' => $order->getTrackingNumber(),
'estimated_delivery' => $order->getEstimatedDelivery()
],
['channels' => ['email', 'database']]
);
return $this->json(['success' => true]);
}
}
Scheduled Notifications
// Schedule reminder notifications
$reminderDate = (new DateTime())->add(new DateInterval('P7D'));
$notificationService->create(
'subscription_expiry_reminder',
$user,
'Your subscription expires soon',
[
'expiry_date' => $user->getSubscriptionExpiry()->format('Y-m-d'),
'renewal_url' => $this->urlGenerator->generate('subscription_renew')
],
[
'channels' => ['email'],
'schedule' => $reminderDate,
'priority' => 'normal'
]
);
Bulk Notifications
// Send notifications to multiple users (loop or create an app helper)
$users = $this->userRepository->findActiveUsers();
foreach ($users as $user) {
$notificationService->send(
'feature_announcement',
$user, // implements Notifiable
'New Feature Available!',
[
'feature_name' => 'Advanced Analytics',
'learn_more_url' => '/features/analytics'
],
['channels' => ['email', 'database']]
);
}
Template System
Email Templates
Templates support HTML and plain text versions with variable substitution:
HTML Template (welcome.html
)
<!DOCTYPE html>
<html>
<head>
<title>Welcome to {{app_name}}</title>
<style>
.container { max-width: 600px; margin: 0 auto; }
.header { background: #007bff; color: white; padding: 20px; }
.content { padding: 20px; }
.button {
display: inline-block;
background: #28a745;
color: white;
padding: 10px 20px;
text-decoration: none;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Welcome, {{user_name}}!</h1>
</div>
<div class="content">
<p>Thank you for joining {{app_name}}. We're excited to have you on board!</p>
<p>To get started, please verify your account:</p>
<p><a href="{{verification_link}}" class="button">Verify Account</a></p>
<p>If you have any questions, don't hesitate to contact our support team.</p>
<p>Best regards,<br>The {{app_name}} Team</p>
</div>
</div>
</body>
</html>
Plain Text Template (welcome.txt
)
Welcome to {{app_name}}, {{user_name}}!
Thank you for joining {{app_name}}. We're excited to have you on board!
To get started, please verify your account by visiting:
{{verification_link}}
If you have any questions, don't hesitate to contact our support team.
Best regards,
The {{app_name}} Team
Template Management
use Glueful\Notifications\Templates\TemplateManager;
$templateManager = container()->get(TemplateManager::class);
// Render template with variables
$rendered = $templateManager->renderTemplate('user_welcome', 'welcome', 'email', [
'user_name' => 'John Doe',
'app_name' => 'Glueful App',
'verification_link' => 'https://example.com/verify/abc123'
]);
// Get a specific template
$template = $templateManager->getTemplate('user_welcome', 'welcome', 'email');
// List all templates
$templates = $templateManager->getAllTemplates();
Dynamic Templates
// Register templates dynamically, then send
$templateManager->createTemplate(
id: 'dynamic_html',
type: 'dynamic_alert',
name: 'dynamic-template',
channel: 'email',
content: '<h1>{{alert_type}} Alert</h1><p>{{message}}</p>'
);
$templateManager->createTemplate(
id: 'dynamic_text',
type: 'dynamic_alert',
name: 'dynamic-template',
channel: 'email_text',
content: '{{alert_type}} ALERT: {{message}}'
);
$notificationService->sendWithTemplate(
'dynamic_alert',
$user,
'dynamic-template',
['alert_type' => 'security', 'message' => 'Suspicious login detected'],
['channels' => ['email']]
);
Advanced Features
Notification Retry System
use Glueful\Notifications\Services\NotificationRetryService;
$retryService = container()->get(NotificationRetryService::class);
// Queue a notification for retry after a failure
$retryService->queueForRetry($notification, $user /* Notifiable */, 'email');
// Process due retries (e.g., from a cron job)
$results = $retryService->processDueRetries(50, $notificationService);
// Configure retry behavior (example)
$retryService->setConfig('retry', [
'max_attempts' => 5,
'delay' => 300,
'backoff' => 'exponential', // or 'linear'
]);
Notification Analytics
// Get delivery statistics
$analytics = $notificationService->getAnalytics([
'start_date' => '2025-01-01',
'end_date' => '2025-01-31',
'channels' => ['email', 'database'],
'types' => ['order_update', 'marketing']
]);
// Response includes:
// - Total notifications sent
// - Delivery rates by channel
// - Open rates (email)
// - Click rates (email)
// - Failure rates and reasons
// - Average delivery time
User Preferences Management
// Get user notification preferences
$preferences = $notificationService->getUserPreferences($userUuid);
// Update preferences
$notificationService->updateUserPreferences($userUuid, [
'email_notifications' => true,
'types' => [
'order_updates' => ['email', 'database'],
'promotions' => ['database'],
'security_alerts' => ['email', 'database'],
'newsletters' => [] // Disabled
],
'quiet_hours' => [
'enabled' => true,
'start' => '22:00',
'end' => '08:00',
'timezone' => 'America/New_York'
]
]);
Notification Queuing
// Queue notifications for batch processing
$notificationService->queue([
'type' => 'weekly_digest',
'recipients' => $activeUsers,
'template' => 'weekly-digest',
'data' => $digestData,
'options' => [
'channels' => ['email'],
'priority' => 'low',
'batch_size' => 100,
'delay' => 3600 // Send in 1 hour
]
]);
Error Handling
Exception Types
use Glueful\Notifications\Exceptions\NotificationException;
use Glueful\Notifications\Exceptions\TemplateNotFoundException;
use Glueful\Notifications\Exceptions\ChannelNotFoundException;
use Glueful\Notifications\Exceptions\DeliveryException;
try {
$notificationService->send($type, $user, $subject, $data);
} catch (TemplateNotFoundException $e) {
// Handle missing template
$this->logger->error('Template not found', [
'template' => $e->getTemplateName(),
'type' => $type
]);
} catch (ChannelNotFoundException $e) {
// Handle invalid channel
$this->logger->error('Channel not found', [
'channel' => $e->getChannelName()
]);
} catch (DeliveryException $e) {
// Handle delivery failure
$this->logger->error('Delivery failed', [
'channel' => $e->getChannel(),
'reason' => $e->getMessage(),
'retryable' => $e->isRetryable()
]);
if ($e->isRetryable()) {
$retryService->scheduleRetry($e->getNotification());
}
} catch (NotificationException $e) {
// Handle general notification error
$this->logger->error('Notification error', [
'error' => $e->getMessage(),
'type' => $type
]);
}
Delivery Status Tracking
// Check delivery status
$status = $notificationService->getDeliveryStatus($notificationUuid);
// Status includes:
// - sent_at: When notification was sent
// - delivered_at: When notification was delivered
// - read_at: When notification was read (if applicable)
// - failed_at: When delivery failed
// - retry_count: Number of retry attempts
// - last_error: Last error message
// - channel_results: Results from each channel
Best Practices
1. Notification Type Naming
Use descriptive, hierarchical naming conventions:
// Good examples
'user.welcome'
'user.password_reset'
'order.created'
'order.shipped'
'order.delivered'
'system.maintenance'
'security.login_alert'
// Avoid generic names
'notification'
'alert'
'message'
2. Template Organization
Organize templates by category and maintain consistency:
storage/templates/email/
├── user/
│ ├── welcome.html
│ ├── welcome.txt
│ ├── password-reset.html
│ └── password-reset.txt
├── order/
│ ├── confirmation.html
│ ├── confirmation.txt
│ ├── shipped.html
│ └── shipped.txt
└── system/
├── maintenance.html
└── maintenance.txt
3. Performance Optimization
// Use queues for bulk notifications
// Implement queuing via your app/job system if needed
// Cache frequently used templates
$templateManager->enableCache(3600);
// Batch database operations
$repository->markMultipleAsRead($notificationUuids);
// Use appropriate indexes
// Database schema should include indexes on:
// - user_uuid, created_at
// - type, created_at
// - read_at (for unread queries)
4. Error Handling and Monitoring
// Implement comprehensive error handling
class NotificationErrorHandler
{
public function handleDeliveryFailure(DeliveryException $e): void
{
// Log error with context
$this->logger->error('Notification delivery failed', [
'notification_id' => $e->getNotificationId(),
'channel' => $e->getChannel(),
'error' => $e->getMessage(),
'user_id' => $e->getUserId(),
'retryable' => $e->isRetryable()
]);
// Schedule retry if appropriate
if ($e->isRetryable() && $e->getAttempts() < 3) {
$this->retryService->scheduleRetry($e->getNotification());
}
// Alert monitoring systems for critical failures
if ($e->isCritical()) {
$this->alertingService->sendAlert('notification_failure', $e);
}
}
}
5. User Experience
// Respect user preferences
$channels = $this->getEffectiveChannels($user, $notificationType);
// Implement quiet hours
if ($this->isQuietHours($user)) {
$options['delay'] = $this->calculateDelayUntilActiveHours($user);
}
// Provide unsubscribe mechanisms
$emailData['unsubscribe_url'] = $this->generateUnsubscribeUrl($user, $type);
// Track user engagement
$this->analyticsService->trackNotificationEngagement($notification, $user);
6. Testing
// Use test channels in development
if (config('app.env') === 'testing') {
$notificationService->setDefaultChannels(['test']);
}
// Mock external services
class MockEmailProvider implements EmailProviderInterface
{
public function send(EmailMessage $message): bool
{
// Store in test database instead of sending
$this->testStorage->store($message);
return true;
}
}
// Test notification flows
class NotificationTest extends TestCase
{
public function testUserWelcomeNotification(): void
{
$user = $this->createTestUser();
$this->notificationService->send('user.welcome', $user, 'Welcome!', []);
$this->assertDatabaseHas('notifications', [
'user_uuid' => $user->getUuid(),
'type' => 'user.welcome'
]);
}
}
This comprehensive notification system provides enterprise-grade features for reliable, scalable notification delivery across multiple channels while maintaining flexibility and ease of use.