Skip to content

abdallahisham/notify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Notify - Laravel Notifications

A powerful Laravel package that extends Laravel's notification system with advanced features for Email, SMS (Taqnyat), and WhatsApp channels.

Features

  • Universal Channel Support: Works with any Laravel notification channel from laravel-notification-channels.com (100+ channels available)
  • Built-in Channels: Email (Mail), SMS (Taqnyat), WhatsApp Business API
  • Flexible Delivery Strategies: Send to all channels or use round-robin fallback
  • Channel Priority/Ordering: Define the order in which channels are attempted
  • Retry Logic: Automatic retry with exponential or linear backoff
  • Rate Limiting: Prevent notification spam
  • User Preferences: Allow users to choose their preferred channels
  • Sandbox Mode: Test notifications without actually sending them
  • Events System: Monitor notification lifecycle with dispatchable events
  • Notification History: Log all sent notifications to database
  • Channel Health Monitoring: Check channel configuration and API connectivity
  • Conditional Channel Rules: Custom logic to determine when each channel should be used
  • Template Management: Named WhatsApp templates with helper methods
  • Localization Support: Multi-language notification support
  • Testing Helpers: Sandbox assertions for easy testing

Installation

Step 1: Install the Package

composer require softlandtech/notify

Step 2: Publish Configuration

php artisan vendor:publish --tag=notify-config

Step 3: (Optional) Publish Migrations

If you want to use notification history:

php artisan vendor:publish --tag=notify-migrations
php artisan migrate

Configuration

The package is configured via config/notify.php and environment variables.

Environment Variables

# Notification Strategy
NOTIFICATION_STRATEGY=all  # or 'round_robin'

# Channels to use (comma-separated)
NOTIFICATION_CHANNELS=mail,sms,whatsapp

# Channel Order
NOTIFICATION_CHANNEL_ORDER=mail,sms,whatsapp

# Sandbox Mode
NOTIFICATION_SANDBOX=false

# Retry Configuration
NOTIFICATION_RETRY_ENABLED=false
NOTIFICATION_RETRY_ATTEMPTS=3
NOTIFICATION_RETRY_BACKOFF=exponential  # or 'linear'
NOTIFICATION_RETRY_DELAY=1000

# Rate Limiting
NOTIFICATION_RATE_LIMIT_ENABLED=true
NOTIFICATION_RATE_LIMIT_MAX=5
NOTIFICATION_RATE_LIMIT_DECAY=1

# Notification History
NOTIFICATION_HISTORY_ENABLED=false
NOTIFICATION_HISTORY_PRUNE_DAYS=30

# User Preferences
NOTIFICATION_USER_PREFERENCES_ENABLED=false

# Events
NOTIFICATION_EVENTS_ENABLED=true

# Taqnyat SMS
TAQNYAT_BASE_URL=https://api.taqnyat.sa
TAQNYAT_TOKEN=your-token
TAQNYAT_SENDER=your-sender

# WhatsApp
WHATSAPP_API_VERSION=v22.0
WHATSAPP_PHONE_NUMBER_ID=your-phone-number-id
WHATSAPP_TOKEN=your-access-token
WHATSAPP_TEMPLATE_NAME=your-default-template
WHATSAPP_TEMPLATE_LANGUAGE=en_US
WHATSAPP_DEFAULT_LANGUAGE=ar

# WhatsApp Templates
WHATSAPP_TEMPLATE_OTP=otp_template
WHATSAPP_TEMPLATE_VERIFICATION=verification_template
WHATSAPP_TEMPLATE_PASSWORD_RESET=password_reset_template

Also configure channels in config/notifications.php:

'channels' => array_filter(array_map('trim', explode(',', env('NOTIFICATION_CHANNELS', 'mail')))),

'channel_mapping' => [
    'mail' => \SoftLandTech\Notify\Channels\MailChannel::class,
    'sms' => \SoftLandTech\Notify\Channels\TaqnyatChannel::class,
    'whatsapp' => \SoftLandTech\Notify\Channels\WhatsappChannel::class,
],

Usage

Basic Notification

Create a notification that extends the package's base class:

<?php

namespace App\Notifications;

use SoftLandTech\Notify\Notification;
use SoftLandTech\Notify\Contracts\MailNotification;
use SoftLandTech\Notify\Contracts\SmsNotification;
use SoftLandTech\Notify\Contracts\WhatsAppNotification;

class WelcomeNotification extends Notification implements MailNotification, SmsNotification, WhatsAppNotification
{
    public function toMail(object $notifiable): array
    {
        return [
            'view' => 'emails.welcome',
            'subject' => 'Welcome to Our App',
            'data' => ['user' => $notifiable],
        ];
    }

    public function toSms(object $notifiable): string
    {
        return 'Welcome to our app!';
    }

    public function toWhatsapp(object $notifiable): array
    {
        return [
            'template_name' => 'welcome_template',
            'language' => 'ar',
            'parameters' => [$notifiable->name],
        ];
    }
}

Important: No need to define the via() method! The package handles channel routing automatically based on configuration.

Sending Notifications

$user->notify(new WelcomeNotification());

Advanced Features

1. Delivery Strategies

Send to All Channels (Default)

NOTIFICATION_STRATEGY=all

All configured channels will receive the notification, regardless of success/failure.

Round-Robin with Fallback

NOTIFICATION_STRATEGY=round_robin

Channels are tried in order. The first successful send stops the process.

You can also override per notification:

class UrgentNotification extends Notification
{
    protected function getStrategy(): string
    {
        return 'round_robin';  // Try channels until one succeeds
    }
}

2. Channel Priority/Ordering

Define the order in which channels are attempted:

NOTIFICATION_CHANNEL_ORDER=whatsapp,sms,mail

Or override per notification:

class SmsFirstNotification extends Notification
{
    protected function getChannelOrder(): array
    {
        return ['sms', 'whatsapp', 'mail'];
    }
}

3. Retry Logic

Enable automatic retries for failed notifications:

NOTIFICATION_RETRY_ENABLED=true
NOTIFICATION_RETRY_ATTEMPTS=3
NOTIFICATION_RETRY_BACKOFF=exponential  # or 'linear'
NOTIFICATION_RETRY_DELAY=1000  # milliseconds

Or customize per notification:

class ImportantNotification extends Notification
{
    protected function shouldRetry(): bool
    {
        return true;
    }

    protected function getRetryConfig(): array
    {
        return [
            'attempts' => 5,
            'backoff' => 'exponential',
            'delay' => 2000,
        ];
    }
}

4. Rate Limiting

Prevent notification spam:

NOTIFICATION_RATE_LIMIT_ENABLED=true
NOTIFICATION_RATE_LIMIT_MAX=5  # Max 5 notifications
NOTIFICATION_RATE_LIMIT_DECAY=1  # Per minute

Override per notification:

class LimitedNotification extends Notification
{
    protected function shouldRateLimit(): bool
    {
        return true;
    }

    protected function getRateLimitConfig(): array
    {
        return [
            'max_attempts' => 3,
            'decay_minutes' => 5,
        ];
    }
}

5. User Channel Preferences

Allow users to control which channels they receive notifications on:

NOTIFICATION_USER_PREFERENCES_ENABLED=true

Add a notification_preferences attribute to your User model:

// User model
protected $casts = [
    'notification_preferences' => 'array',
];

// Set user preferences
$user->notification_preferences = ['mail', 'whatsapp'];
$user->save();

6. Sandbox Mode

Test notifications without sending them:

NOTIFICATION_SANDBOX=true
use SoftLandTech\Notify\Channels\SandboxChannel;

// In tests
SandboxChannel::assertSent(WelcomeNotification::class);
SandboxChannel::assertSent(WelcomeNotification::class, function ($notification, $notifiable) use ($user) {
    return $notifiable->id === $user->id;
});
SandboxChannel::assertNotSent(SpamNotification::class);
SandboxChannel::clear();  // Clear sandbox history

7. Events System

Listen to notification events:

use SoftLandTech\Notify\Events\NotificationSending;
use SoftLandTech\Notify\Events\NotificationSent;
use SoftLandTech\Notify\Events\NotificationFailed;
use SoftLandTech\Notify\Events\ChannelSkipped;
use SoftLandTech\Notify\Events\RoundRobinFallback;

Event::listen(NotificationSent::class, function ($event) {
    Log::info('Notification sent', [
        'notification' => get_class($event->notification),
        'channel' => $event->channel,
    ]);
});

8. Notification History

Enable database logging of all notifications:

NOTIFICATION_HISTORY_ENABLED=true
php artisan vendor:publish --tag=notify-migrations
php artisan migrate

Query notification history:

use SoftLandTech\Notify\Models\NotificationHistory;

// Get all sent notifications for a user
$history = NotificationHistory::where('notifiable_type', User::class)
    ->where('notifiable_id', $user->id)
    ->where('status', 'sent')
    ->get();

// Get failed notifications
$failed = NotificationHistory::where('status', 'failed')->get();

9. Channel Health Monitoring

Check if your notification channels are properly configured:

# Check all channels
php artisan notifications:check-health

# Check specific channel
php artisan notifications:check-health mail
php artisan notifications:check-health sms
php artisan notifications:check-health whatsapp

Programmatically:

use SoftLandTech\Notify\ChannelHealthMonitor;

$monitor = app(ChannelHealthMonitor::class);

$results = $monitor->checkAll();
// ['mail' => [...], 'sms' => [...], 'whatsapp' => [...]]

$mailHealth = $monitor->checkMail();

10. Conditional Channel Rules

Implement custom logic to determine when each channel should be used:

class PremiumNotification extends Notification
{
    protected function shouldUseChannel(string $channelName, object $notifiable): bool
    {
        // Only use WhatsApp for premium users
        if ($channelName === 'whatsapp') {
            return $notifiable->isPremium();
        }

        // Only send emails during business hours
        if ($channelName === 'mail') {
            return now()->hour >= 9 && now()->hour < 17;
        }

        return true;
    }
}

11. Template Management

Use the template management trait for WhatsApp notifications:

use SoftLandTech\Notify\Notification;
use SoftLandTech\Notify\Concerns\HasTemplates;
use SoftLandTech\Notify\Contracts\WhatsAppNotification;

class OtpNotification extends Notification implements WhatsAppNotification
{
    use HasTemplates;

    public function __construct(public string $code) {}

    public function toWhatsapp(object $notifiable): array
    {
        // Helper method for OTP templates
        return $this->buildOtpTemplate($this->code, 5);
    }
}

Available template helpers:

$this->buildOtpTemplate($code, $expiryMinutes);
$this->buildVerificationTemplate($code);
$this->buildPasswordResetTemplate($code, $expiryMinutes);
$this->buildTemplate('custom_template', ['param1', 'param2'], 'ar');
$this->getTemplate('otp');  // Gets template name from config

12. Localization Support

Use the localization trait for multi-language support:

use SoftLandTech\Notify\Notification;
use SoftLandTech\Notify\Concerns\Localizable;
use SoftLandTech\Notify\Contracts\MailNotification;

class LocalizedNotification extends Notification implements MailNotification
{
    use Localizable;

    public function toMail(object $notifiable): array
    {
        return $this->withLocale($notifiable, function () use ($notifiable) {
            return [
                'view' => 'emails.welcome',
                'subject' => $this->trans('notifications.welcome.subject', [], $notifiable),
                'data' => [
                    'message' => $this->trans('notifications.welcome.message', ['name' => $notifiable->name]),
                ],
            ];
        });
    }
}

The trait will automatically detect the user's locale from:

  1. $notifiable->locale attribute
  2. $notifiable->preferredLocale() method
  3. Application default locale

Adding Community Notification Channels

The package supports any Laravel notification channel from laravel-notification-channels.com. All features (retry, rate limiting, sandbox mode, events, etc.) automatically work with any channel!

Example: Adding Slack

  1. Install the Slack channel:
composer require laravel/slack-notification-channel
  1. Add to config/notifications.php:
'channels' => array_filter(array_map('trim', explode(',', env('NOTIFICATION_CHANNELS', 'mail,slack')))),

'channel_mapping' => [
    'mail' => \SoftLandTech\Notify\Channels\MailChannel::class,
    'sms' => \SoftLandTech\Notify\Channels\TaqnyatChannel::class,
    'whatsapp' => \SoftLandTech\Notify\Channels\WhatsappChannel::class,
    'slack' => \Illuminate\Notifications\Slack\SlackChannel::class, // Add this
],
  1. Update your .env:
NOTIFICATION_CHANNELS=mail,slack
SLACK_WEBHOOK_URL=your-webhook-url
  1. Use in notifications:
use SoftLandTech\Notify\Notification;
use SoftLandTech\Notify\Contracts\MailNotification;
use Illuminate\Notifications\Slack\SlackMessage;

class OrderShipped extends Notification implements MailNotification
{
    public function toMail(object $notifiable): array
    {
        return [
            'view' => 'emails.order-shipped',
            'subject' => 'Your order has shipped!',
        ];
    }

    public function toSlack(object $notifiable): SlackMessage
    {
        return (new SlackMessage)
            ->content('Your order has shipped!');
    }
}

// Add routing in your User model
public function routeNotificationFor(string $channel): mixed
{
    return match($channel) {
        'mail' => $this->email,
        'slack' => env('SLACK_WEBHOOK_URL'),
        'sms', 'whatsapp' => $this->phone,
        default => null,
    };
}

That's it! Slack notifications now have retry logic, rate limiting, sandbox mode, and all other features.

More Channel Examples

Discord

composer require laravel-notification-channels/discord
'discord' => \NotificationChannels\Discord\DiscordChannel::class,

Telegram

composer require laravel-notification-channels/telegram
'telegram' => \NotificationChannels\Telegram\TelegramChannel::class,

Vonage (SMS)

composer require laravel/vonage-notification-channel
'vonage' => \Illuminate\Notifications\VonageChannel::class,

Twilio

composer require laravel-notification-channels/twilio
'twilio' => \NotificationChannels\Twilio\TwilioChannel::class,

Firebase (Push Notifications)

composer require laravel-notification-channels/fcm
'fcm' => \NotificationChannels\Fcm\FcmChannel::class,

Browse All Channels

Visit laravel-notification-channels.com to see 100+ available channels including:

  • Social: Facebook, Twitter, LinkedIn
  • Messaging: Discord, Telegram, Slack, Microsoft Teams
  • SMS: Twilio, Nexmo, Vonage, MessageBird
  • Push: Firebase, OneSignal, Pusher
  • And many more!

Facades

The package provides convenient facades:

use Taqnyat;
use Whatsapp;

// Send SMS via Taqnyat
Taqnyat::sendSms('966500000000', 'Your OTP is 123456');

// Send WhatsApp template
Whatsapp::sendTemplate('966500000000', [
    'template_name' => 'otp',
    'language' => 'ar',
    'parameters' => ['123456', '5'],
]);

Testing

Unit Tests

vendor/bin/pest

Using Sandbox Mode in Tests

use SoftLandTech\Notify\Channels\SandboxChannel;

class NotificationTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        config(['notify.sandbox' => true]);
        SandboxChannel::clear();
    }

    public function test_welcome_notification_is_sent()
    {
        $user = User::factory()->create();

        $user->notify(new WelcomeNotification());

        SandboxChannel::assertSent(WelcomeNotification::class, function ($notification, $notifiable) use ($user) {
            return $notifiable->id === $user->id;
        });
    }
}

Customization

Custom Notification Stub

The package modifies the make:notification stub to automatically extend the package's base class:

php artisan make:notification MyNotification

This will generate:

<?php

namespace App\Notifications;

use SoftLandTech\Notify\Notification;
use Illuminate\Bus\Queueable;

class MyNotification extends Notification
{
    use Queueable;

    // No via() method needed!
}

Events Reference

Event Properties Description
NotificationSending $notification, $notifiable, $channel Fired before sending
NotificationSent $notification, $notifiable, $channel, $response Fired after successful send
NotificationFailed $notification, $notifiable, $channel, $exception Fired on failure
ChannelSkipped $notification, $notifiable, $channel, $reason Fired when a channel is skipped
RoundRobinFallback $notification, $notifiable, $failedChannel, $nextChannel, $exception Fired during round-robin fallback

Architecture

The package uses the Decorator pattern to layer features around any Laravel notification channel:

SandboxChannel (if enabled)
  └─ RateLimitedChannel (if enabled)
      └─ RetryableChannel (if enabled)
          └─ Any Channel (MailChannel, SlackChannel, DiscordChannel, etc.)

This architecture ensures that:

  • All features (retry, rate limiting, sandbox, events, history) work with any Laravel notification channel
  • Features can be enabled/disabled independently
  • New channels can be added instantly without code changes
  • The decorator layers are applied automatically to all channels

Simply add any community channel to your channel_mapping config and all features will work immediately!

License

This package is proprietary software.

Credits

Created by Abdallah Isham

About

Empower your laravel notifications

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages