PHP code example of andydefer / laravel-notification

1. Go to this page and download the library: Download andydefer/laravel-notification library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

andydefer / laravel-notification example snippets




namespace App\Models;

use AndyDefer\LaravelNotification\Contracts\NotifiableInterface;
use AndyDefer\LaravelNotification\Collections\NotificationRouteCollection;
use AndyDefer\LaravelNotification\ValueObjects\NotificationRouteVO;
use AndyDefer\LaravelNotification\Channels\MailChannel;
use AndyDefer\LaravelNotification\Channels\DatabaseChannel;
use AndyDefer\LaravelNotification\Channels\PusherChannel;
use AndyDefer\LaravelNotification\Channels\WebPushChannel;
use AndyDefer\LaravelNotification\Channels\FirebaseCloudMessagingChannel;
use AndyDefer\DomainStructures\Utils\StrictDataObject;
use Illuminate\Database\Eloquent\Model;

class User extends Model implements NotifiableInterface
{
    public function getNotificationChannels(): NotificationRouteCollection
    {
        $collection = new NotificationRouteCollection;

        // ✅ Email principal
        if ($this->email_primary) {
            $collection->add(new NotificationRouteVO(
                channelClass: MailChannel::class,
                destination: $this->email_primary,
                metadata: new StrictDataObject(['type' => 'primary'])
            ));
        }

        // ✅ Email secondaire
        if ($this->email_secondary) {
            $collection->add(new NotificationRouteVO(
                channelClass: MailChannel::class,
                destination: $this->email_secondary,
                metadata: new StrictDataObject(['type' => 'secondary'])
            ));
        }

        // ✅ Pusher (temps réel)
        $collection->add(new NotificationRouteVO(
            channelClass: PusherChannel::class,
            destination: "private-user.{$this->id}",
            metadata: new StrictDataObject(['event' => 'notification.received'])
        ));

        // ✅ Web Push (navigateur)
        foreach ($this->webPushSubscriptions as $subscription) {
            $collection->add(new NotificationRouteVO(
                channelClass: WebPushChannel::class,
                destination: $subscription->endpoint,
                metadata: new StrictDataObject([
                    'endpoint' => $subscription->endpoint,
                    'p256dh' => $subscription->p256dh,
                    'auth' => $subscription->auth,
                ])
            ));
        }

        // ✅ FCM (mobile)
        foreach ($this->fcmDevices as $device) {
            $collection->add(new NotificationRouteVO(
                channelClass: FirebaseCloudMessagingChannel::class,
                destination: $device->token,
                metadata: new StrictDataObject([
                    'type' => 'fcm',
                    'device_id' => $device->device_id,
                ])
            ));
        }

        // ✅ Base de données (traçabilité)
        $collection->add(new NotificationRouteVO(
            channelClass: DatabaseChannel::class,
            destination: 'database'
        ));

        return $collection;
    }

    public function getMorphClass(): string
    {
        return 'user';
    }

    public function getKey(): int
    {
        return $this->id;
    }
}

new NotificationRouteVO(
    channelClass: MailChannel::class,
    destination: '[email protected]',
    metadata: new StrictDataObject(['type' => 'primary'])
);



use AndyDefer\LaravelNotification\Services\NotificationService;
use AndyDefer\LaravelNotification\Records\SendNowRecord;
use AndyDefer\LaravelNotification\ValueObjects\NotificationMessageVO;
use AndyDefer\LaravelNotification\ValueObjects\MessageBodyVO;
use AndyDefer\LaravelNotification\ValueObjects\MessageSubjectVO;
use AndyDefer\LaravelNotification\Channels\MailChannel;
use AndyDefer\LaravelNotification\Channels\PusherChannel;
use AndyDefer\DomainStructures\Utils\StrictDataObject;

class UserController extends Controller
{
    public function __construct(
        private readonly NotificationService $service
    ) {}

    public function welcome(User $user)
    {
        $message = new NotificationMessageVO(
            body: new MessageBodyVO('<h1>Bonjour !</h1><p>Bienvenue sur notre plateforme.</p>'),
            subject: new MessageSubjectVO('Bienvenue !'),
            type: 'welcome',
            data: new StrictDataObject(['user_id' => $user->id])
        );

        $record = SendNowRecord::from([
            'channels' => [MailChannel::class, PusherChannel::class],
            'limit_per_channel' => 1,
        ]);

        $results = $this->service->sendNow($user, $message, $record);

        return response()->json([
            'success' => $results->allSuccess(),
            'sent' => $results->getSuccessCount(),
            'failed' => $results->getFailureCount(),
        ]);
    }
}

use AndyDefer\LaravelNotification\Records\SendLaterRecord;

$record = SendLaterRecord::from([
    'delay_seconds' => 1800,
    'channels' => [MailChannel::class, PusherChannel::class],
    'limit_per_channel' => 1,
]);

$alias = $this->service->sendLater($user, $message, $record);

use AndyDefer\LaravelNotification\Records\SendAtRecord;
use AndyDefer\LaravelNotification\ValueObjects\NotificationDateTimeVO;

$record = SendAtRecord::from([
    'scheduled_at' => new NotificationDateTimeVO($scheduledAt->toIso8601String()),
    'channels' => [MailChannel::class],
    'limit_per_channel' => 1,
]);

$alias = $this->service->sendAt($user, $message, $record);

use AndyDefer\LaravelNotification\Records\SendRecurringRecord;
use AndyDefer\Task\ValueObjects\MaxFailedAttemptsVO;

$record = SendRecurringRecord::from([
    'interval_seconds' => 604800,
    'start_at' => new NotificationDateTimeVO('2026-07-08 09:00:00'),
    'end_at' => new NotificationDateTimeVO('2026-12-31 09:00:00'),
    'channels' => [MailChannel::class],
    'limit_per_channel' => 1,
    'max_attempts' => new MaxFailedAttemptsVO(3),
]);

$alias = $this->service->sendRecurring($user, $message, $record);

use AndyDefer\LaravelNotification\Options\SendOptions;

$options = SendOptions::init()
    ->withChannel(MailChannel::class)
    ->withDestinationFilter(MailChannel::class, '[email protected]')
    ->withLimitPerChannel(1);

$results = $notificationService
    ->withOptions($options)
    ->sendNow($user, $message);

use AndyDefer\LaravelNotification\Builders\NotifiableBuilder;

$results = NotifiableBuilder::create()
    ->to(MailChannel::class, '[email protected]')
    ->to(PusherChannel::class, 'private-user.42')
    ->subject('Notification importante')
    ->body('Votre commande a été expédiée.')
    ->data(['order_id' => 12345])
    ->sendNow();

use AndyDefer\LaravelNotification\ValueObjects\MessageViewBodyVO;

$body = MessageViewBodyVO::html(
    view: 'emails.welcome',
    data: ['user' => $user]
);

$finalBody = $body->withData(['extra' => 'value']);

class TaskManager
{
    public function __construct(
        private readonly NotificationService $service
    ) {}

    public function pause(string $alias): bool
    {
        return $this->service->pause($alias);
    }

    public function resume(string $alias): bool
    {
        return $this->service->resume($alias);
    }

    public function changeInterval(string $alias, int $newIntervalSeconds): bool
    {
        return $this->service->changeInterval($alias, $newIntervalSeconds);
    }

    public function cancel(string $alias): bool
    {
        return $this->service->cancel($alias);
    }
}

$stats = $this->service->getStats($user);

return response()->json([
    'total' => $stats->total,
    'sent' => $stats->sent,
    'failed' => $stats->failed,
    'delivered' => $stats->delivered,
    'pending' => $stats->pending,
    'success_rate' => $stats->success_rate . '%',
]);

// config/notification.php
'channels' => [
    'mail' => [
        'enabled' => true,
        'default_from' => env('MAIL_FROM_ADDRESS'),
        'default_from_name' => env('MAIL_FROM_NAME'),
    ],
],

$record = SendNowRecord::from([
    'channels' => [MailChannel::class],
    'limit_per_channel' => 1,
]);

$results = $service->sendNow($user, $message, $record);

$collection->add(new NotificationRouteVO(
    DatabaseChannel::class,
    'database'
));

// config/notification.php
'pusher' => [
    'enabled' => env('PUSHER_NOTIFICATION_ENABLED', false),
    'app_id' => env('PUSHER_APP_ID'),
    'key' => env('PUSHER_APP_KEY'),
    'secret' => env('PUSHER_APP_SECRET'),
    'cluster' => env('PUSHER_APP_CLUSTER', 'eu'),
    'use_tls' => env('PUSHER_USE_TLS', true),
    'timeout' => env('PUSHER_TIMEOUT', 30),
    'default_channel' => env('PUSHER_NOTIFICATION_CHANNEL', 'notifications'),
],

$collection->add(new NotificationRouteVO(
    PusherChannel::class,
    "private-user.{$this->id}",
    new StrictDataObject(['event' => 'notification.received'])
));

// config/notification.php
'webpush' => [
    'enabled' => env('WEBPUSH_NOTIFICATION_ENABLED', false),
    'subject' => env('WEBPUSH_SUBJECT'),
    'public_key' => env('WEBPUSH_PUBLIC_KEY'),
    'private_key' => env('WEBPUSH_PRIVATE_KEY'),
    'ttl' => env('WEBPUSH_TTL', 3600),
    'urgency' => env('WEBPUSH_URGENCY', 'normal'),
    'topic' => env('WEBPUSH_TOPIC', 'notification'),
],

final class WebPushSubscription extends Model implements NotifiableInterface, PingableInterface
{
    use HasPingPong;
    use HasUuids;

    protected $table = 'web_push_subscriptions';
    protected $keyType = 'string';
    public $incrementing = false;

    protected $fillable = [
        'id', 'endpoint', 'p256dh', 'auth', 'browser',
        'user_agent', 'last_seen_at', 'notifiable_type', 'notifiable_id',
    ];

    protected $casts = ['last_seen_at' => 'immutable_datetime'];

    public function notifiable(): MorphTo
    {
        return $this->morphTo();
    }

    public function getNotificationChannels(): NotificationRouteCollection
    {
        $collection = new NotificationRouteCollection;

        $collection->add(new NotificationRouteVO(
            channelClass: WebPushChannel::class,
            destination: (string) $this->endpoint,
            metadata: new StrictDataObject([
                'type' => 'webpush',
                'endpoint' => (string) $this->endpoint,
                'p256dh' => (string) $this->p256dh,
                'auth' => (string) $this->auth,
            ]),
        ));

        return $collection;
    }
}

$record = SendNowRecord::from([
    'channels' => [WebPushChannel::class],
    'limit_per_channel' => 1,
]);

$results = $service->sendNow($subscription, $message, $record);

// config/notification.php
'firebase' => [
    'enabled' => env('FIREBASE_NOTIFICATION_ENABLED', false),
    'credentials_path' => env('FIREBASE_CREDENTIALS_PATH'),
    'project_id' => env('FIREBASE_PROJECT_ID'),
    'scope' => env('FIREBASE_SCOPE', 'https://www.googleapis.com/auth/firebase.messaging'),
    'timeout' => env('FIREBASE_TIMEOUT', 30),
],

final class FcmDevice extends Model implements NotifiableInterface, PingableInterface
{
    use HasPingPong;
    use HasUuids;

    protected $table = 'fcm_devices';
    protected $keyType = 'string';
    public $incrementing = false;

    protected $fillable = [
        'id', 'device_id', 'token', 'platform', 'user_agent',
        'last_seen_at', 'notifiable_type', 'notifiable_id',
    ];

    protected $casts = ['last_seen_at' => 'immutable_datetime'];

    public function notifiable(): MorphTo
    {
        return $this->morphTo();
    }

    public function getNotificationChannels(): NotificationRouteCollection
    {
        $collection = new NotificationRouteCollection;

        $collection->add(new NotificationRouteVO(
            channelClass: FirebaseCloudMessagingChannel::class,
            destination: (string) $this->token,
            metadata: new StrictDataObject([
                'type' => 'fcm',
                'device_id' => (string) $this->device_id,
            ]),
        ));

        return $collection;
    }
}

$record = SendNowRecord::from([
    'channels' => [FirebaseCloudMessagingChannel::class],
    'limit_per_channel' => 1,
]);

$results = $service->sendNow($device, $message, $record);

interface PingPongInterface
{
    public function ping(Model $notifiable): PingStatus;
    public function isAlive(Model $notifiable): bool;
    public function pingOrPrune(Model $notifiable): PingStatus;
}

enum PingStatus: string
{
    case PONG = 'pong';
    case INVALID = 'invalid';

    public function isPong(): bool;
    public function isInvalid(): bool;
}

final class FcmDevice extends Model implements NotifiableInterface, PingableInterface
{
    use HasPingPong;
    // ...
}

final class WebPushSubscription extends Model implements NotifiableInterface, PingableInterface
{
    use HasPingPong;
    // ...
}

final class PingPongAdapter
{
    private const HELPERS = [
        FcmDevice::class => FcmPingPong::class,
        WebPushSubscription::class => WebPushPingPong::class,
    ];

    public function for(Model $model): PingPongInterface;
}

// Vérifier une cible
if ($subscription->isAlive()) {
    // Toujours joignable
}

// Vérifier et supprimer si invalide
$status = $subscription->pingOrPrune();

if ($status->isInvalid()) {
    logger()->info('Subscription pruned', ['id' => $subscription->id]);
}

class DiscordDriver extends AbstractDriver
{
    public function __construct(private readonly array $config) {}

    public function getChannel(): string
    {
        return 'discord';
    }

    public function validateConfiguration(): bool
    {
        return !empty($this->config['webhook_url']);
    }

    protected function execute(
        NotificationMessageVO $message,
        NotificationRouteVO $route
    ): bool {
        $webhookUrl = $route->getMetadata()?->get('webhook_url')
            ?? $this->config['webhook_url'];

        $response = Http::post($webhookUrl, [
            'content' => $message->getBodyValue(),
        ]);

        if (!$response->successful()) {
            throw new RuntimeException('Discord API error: ' . $response->body());
        }

        return true;
    }
}

class DiscordChannel extends AbstractChannel
{
    public function getName(): string { return 'discord'; }
    public function getLabel(): string { return 'Discord'; }
    public function getIcon(): string { return '🎮'; }

    public function isEnabled(): bool
    {
        return $this->configRepository->get('notification.channels.discord.enabled', false);
    }

    public function getConfig(): AbstractRecord
    {
        return DiscordConfigRecord::from(
            $this->configRepository->get('notification.channels.discord', [])
        );
    }

    public function createDriver(): AbstractDriver
    {
        return new DiscordDriver($this->getConfig()->toArray());
    }

    public static function validateDestination(string $destination): bool
    {
        return filter_var($destination, FILTER_VALIDATE_URL) !== false
            && str_contains($destination, 'discord.com/api/webhooks');
    }
}

final class DiscordConfigRecord extends AbstractRecord
{
    public function __construct(
        public readonly bool $enabled = false,
        public readonly ?string $webhook_url = null,
    ) {}
}

class Doctor extends Model implements NotifiableInterface
{
    public function getNotificationChannels(): NotificationRouteCollection
    {
        $collection = new NotificationRouteCollection;

        if ($this->email_professional) {
            $collection->add(new NotificationRouteVO(
                MailChannel::class,
                $this->email_professional,
                new StrictDataObject(['priority' => 'high'])
            ));
        }

        if ($this->email_personal) {
            $collection->add(new NotificationRouteVO(
                MailChannel::class,
                $this->email_personal,
                new StrictDataObject(['priority' => 'low'])
            ));
        }

        $collection->add(new NotificationRouteVO(
            DatabaseChannel::class,
            'database'
        ));

        return $collection;
    }
}

class Order extends Model implements NotifiableInterface
{
    public function getNotificationChannels(): NotificationRouteCollection
    {
        $collection = new NotificationRouteCollection;

        $collection->add(new NotificationRouteVO(MailChannel::class, $this->customer_email));
        $collection->add(new NotificationRouteVO(PusherChannel::class, "private-order.{$this->id}"));

        foreach ($this->customer->webPushSubscriptions as $subscription) {
            $collection->add(new NotificationRouteVO(
                WebPushChannel::class,
                $subscription->endpoint,
                new StrictDataObject([
                    'endpoint' => $subscription->endpoint,
                    'p256dh' => $subscription->p256dh,
                    'auth' => $subscription->auth,
                ])
            ));
        }

        foreach ($this->customer->fcmDevices as $device) {
            $collection->add(new NotificationRouteVO(
                FirebaseCloudMessagingChannel::class,
                $device->token,
                new StrictDataObject([
                    'device_id' => $device->device_id,
                ])
            ));
        }

        return $collection;
    }
}

$message = new NotificationMessageVO(
    body: new MessageBodyVO('Votre commande a été expédiée.'),
    subject: new MessageSubjectVO('Commande expédiée'),
    type: 'order_shipped',
    data: new StrictDataObject(['order_id' => 42, 'url' => '/orders/42']),
);

$record = SendNowRecord::from([
    'channels' => [
        PusherChannel::class,
        WebPushChannel::class,
        FirebaseCloudMessagingChannel::class,
        DatabaseChannel::class,
    ],
    'limit_per_channel' => 1,
]);

$results = $service->sendNow($order->customer, $message, $record);

class UserController extends Controller
{
    public function __construct(
        private readonly NotificationService $service
    ) {}
}

if ($this->email) {
    $collection->add(new NotificationRouteVO(MailChannel::class, $this->email));
}

$results = NotifiableBuilder::create()
    ->to(MailChannel::class, '[email protected]')
    ->subject('Test')
    ->body('Contenu')
    ->sendNow();

$collection->add(new NotificationRouteVO(
    DatabaseChannel::class,
    'database'
));

$record = SendNowRecord::from([
    'channels' => [MailChannel::class, PusherChannel::class],
    'limit_per_channel' => 1,
]);

$results = $this->service->sendNow($user, $message, $record);

if (!$results->allSuccess()) {
    foreach ($results->getFailures() as $failure) {
        Log::error('Notification failed', [
            'channel' => $failure->channel->getValue(),
            'destination' => $failure->destination,
            'error' => $failure->error_message->getValue(),
        ]);
    }
}

final class User extends Authenticatable
{
    use HasNotifications;
}
bash
composer  vendor:publish --tag=notification-migrations
php artisan migrate

php artisan vendor:publish --tag=notification-config
bash
php artisan vendor:publish --tag=notification-migrations
php artisan migrate
bash
php artisan vendor:publish --tag=notification-migrations
php artisan migrate