Skip to main content
Version: 5.x

Notification System

Introduction

About the messaging system

BowPHP's messaging system is a powerful tool that lets you send notifications through different channels (email, database, SMS). It is designed to be flexible, extensible, and easy to use.

Whether you need to send welcome emails, activity notifications, or system alerts, the messaging system has you covered.

Configuration

Preparing the Model

To get started, your model must use the WithNotifier trait. This trait adds all the methods needed to send notifications.

use Bow\Notifier\WithNotifier;
use Bow\Database\Barry\Model;

class User extends Model
{
use WithNotifier;
}

Creating Notifications

Basic Structure

A notification is a class that extends Notifier. Here is a complete example of a welcome notification:

use Bow\Notifier\Notifier;
use Bow\Mail\Envelop;
use Bow\Database\Barry\Model;

class WelcomeNotifier extends Notifier
{
/**
* Constructor - passes data to the notification
*/
public function __construct(
private string $customMessage = "Bienvenue!"
) {
}

/**
* Configures the email message
*/
public function toMail(Model $context): ?Envelop
{
return (new Envelop())
->to($context->email)
->subject('Bienvenue sur notre plateforme!')
->view('emails.welcome', [
'user' => $context,
'message' => $this->customMessage
]);
}

/**
* Configures the database notification
*/
public function toDatabase(Model $context): array
{
return [
'type' => 'welcome_notification',
'data' => [
'user_id' => $context->id,
'message' => $this->customMessage,
'created_at' => now()
]
];
}

/**
* Defines the channels to use
*/
public function channels(Model $context): array
{
// You can add conditional logic
if ($context->preferences['email_notifications']) {
return ['mail', 'database'];
}

return ['database'];
}
}

Common Notification Examples

Password Reset Notification

class PasswordResetNotifier extends Notifier
{
public function __construct(
private string $token
) {
}

public function toMail(Model $context): Envelop
{
$resetUrl = url("/password/reset/{$this->token}");

return (new Envelop())
->to($context->email)
->subject('Réinitialisation de votre mot de passe')
->view('emails.password-reset', [
'user' => $context,
'resetUrl' => $resetUrl,
'expiresIn' => '60 minutes'
]);
}

public function channels(Model $context): array
{
return ['mail'];
}
}

Activity Notification

class NewCommentNotifier extends Notifier
{
public function __construct(
private array $commentData
) {
}

public function toMail(Model $context): Envelop
{
return (new Envelop())
->to($context->email)
->subject('Nouveau commentaire sur votre post')
->view('emails.new-comment', [
'user' => $context,
'comment' => $this->commentData
]);
}

public function toDatabase(Model $context): array
{
return [
'type' => 'new_comment',
'data' => [
'post_id' => $this->commentData['post_id'],
'comment_id' => $this->commentData['id'],
'commenter' => $this->commentData['user_name'],
'excerpt' => substr($this->commentData['content'], 0, 100)
]
];
}

public function channels(Model $context): array
{
return ['mail', 'database'];
}
}

Sending Notifications

Simple Sending

Simple sending is synchronous and immediate:

// Send a basic welcome notification
$user->sendMessage(new WelcomeNotifier());

// Send with a custom message
$user->sendMessage(new WelcomeNotifier("Ravi de vous avoir parmi nous!"));

// Send a password reset notification
$user->sendMessage(new PasswordResetNotifier($token));

// Send a new comment notification
$user->sendMessage(new NewCommentNotifier([
'id' => 1,
'post_id' => 123,
'user_name' => 'John Doe',
'content' => 'Super article!'
]));

Queued Sending

Optimal performance

Queued sending is asynchronous and delivers better performance. Use this method for non-critical notifications.

// Default queue
$user->setMessageQueue(new WelcomeNotifier());

// Specific queue with priority
$user->sendMessageQueueOn('high-priority', new PasswordResetMessage($token));

// Delayed sending (useful for reminders)
$user->sendMessageLater(
3600, // delay in seconds (1 hour)
new ReminderMessage("N'oubliez pas de compléter votre profil!")
);

// Delayed sending on a specific queue
$user->sendMessageLaterOn(
1800, // 30 minutes
'reminders',
new ReminderMessage("Finalisez votre commande!")
);

Notification Channels

BowPHP ships with five channels by default. Each one corresponds to a to<Channel>() method that you may (or may not) implement in your notification:

ChannelKeyMethod to implementBundled adapter
EmailmailtoMail(Model)MailChannelAdapter
DatabasedatabasetoDatabase(Model)DatabaseChannelAdapter
SMSsmstoSms(Model)SmsChannelAdapter
SlackslacktoSlack(Model)SlackChannelAdapter
TelegramtelegramtoTelegram(Model)TelegramChannelAdapter

Your notification's channels(Model) method decides which channels to enable for a given context. Only the channels it returns call their to<Channel>() method; the other to* methods inherited from Notifier remain unused.

Email

The email channel is perfect for important communications. Here is a complete example of an email notification:

public function toMail(Model $context): Envelop
{
return (new Envelop())
->to($context->email)
->cc('support@example.com')
->bcc('archives@example.com')
->subject('Sujet Important')
->view('emails.template', [
'user' => $context,
'data' => $this->data
])
->attach('/chemin/vers/fichier.pdf')
->priority('high');
}

Database

The database channel is ideal for in-app notifications.

Table structure

Here is the complete structure of the notifications table required for the database channel:

CREATE TABLE notifications (
id CHAR(36) PRIMARY KEY,
concern_id BIGINT NOT NULL,
concern_type VARCHAR(255) NOT NULL,
type VARCHAR(255) NOT NULL,
data JSON NOT NULL,
read_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Advanced usage example:

public function toDatabase(Model $context): array
{
return [
'type' => 'system_notification',
'data' => [
'title' => 'Mise à jour Système',
'message' => 'Une nouvelle version est disponible',
'action_url' => '/settings/updates',
'action_text' => 'Mettre à jour maintenant',
'severity' => 'high',
'icon' => 'update-icon',
'metadata' => [
'version' => '2.0.0',
'changes' => ['feature1', 'feature2']
]
]
];
}

Extending the System

Extensibility

You can create your own custom channels to send notifications through other means (SMS, Slack, Push, etc.).

Creating a New Channel

A custom channel is a class that implements Bow\Notifier\Contracts\ChannelAdapterInterface and exposes a send(Model $context, Notifier $notifier): void method.

Example of a channel that logs notifications:

namespace App\Notifier\Channels;

use Bow\Database\Barry\Model;
use Bow\Notifier\Contracts\ChannelAdapterInterface;
use Bow\Notifier\Notifier;

class LogChannel implements ChannelAdapterInterface
{
public function send(Model $context, Notifier $notifier): void
{
if (!method_exists($notifier, 'toLog')) {
throw new \InvalidArgumentException('The notifier must define a toLog method.');
}

$log = $notifier->toLog($context);

logger()->log($log);
}
}

Registering the New Channel

To register your new channel, you must add it to the application configuration. In your App\Configurations\ApplicationConfiguration.php file, add the following code to the create() method:

app/Configurations/ApplicationConfiguration.php
public function create(Loader $config): void  
{
Notifier::pushChannels([
'log' => LogChannel::class
]);
}

Best Practices

Important recommendations

Follow these best practices for a robust and high-performing messaging system.

  1. Segmentation - Create specific notifications for each use case
  2. Queues - Use queues for non-urgent notifications
  3. Personalization - Let users choose their preferred channels
  4. Testing - Test your notifications with real-world cases
  5. Monitoring - Watch for sending failures and put retry strategies in place

Debugging

To debug your notifications, you can:

// Log the sends
Log::info('Envoi de notification', [
'type' => get_class($notification),
'user' => $context->id,
'channels' => $notification->channels($context)
]);

// Test in a local environment (the second argument writes to the config)
config('mail.default', 'log');

Is something missing?

If you run into problems with the documentation or have suggestions to improve the documentation or the project in general, please open an issue for us, or send a tweet mentioning the Twitter account @bowframework or directly on github.