symfony/notifier
Symfony Notifier sends notifications through multiple channels like email, SMS, chat apps, and push. It unifies transports and routing so you can dispatch messages to users via one or more providers with a consistent API.
composer require symfony/notifier
config/services.php:
'notifier' => [
'dsn' => 'sms://default:YOUR_TWILIO_AUTH_TOKEN@default?from=+1234567890',
],
app/Notifications/SmsNotification.php):
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Notification\SmsNotification;
class SmsNotification extends Notification
{
public function __construct(private string $message)
{
}
public function asSms(): SmsNotification
{
return SmsNotification::create($this->message)
->context(['user_id' => 123]);
}
}
use Symfony\Component\Notifier\NotifierInterface;
use Symfony\Component\Notifier\Bridge\SymfonyMessenger\Transport\SymfonyMessengerTransportFactory;
public function sendNotification(NotifierInterface $notifier)
{
$notification = new SmsNotification('Your order is confirmed!');
$notifier->send($notification);
}
// In a Laravel controller or command
$notifier = app(NotifierInterface::class);
$notification = new SmsNotification("Reset your password: http://example.com/reset/{$token}");
$notifier->send($notification);
Notification class and extend for channel-specific logic.
class BaseNotification extends Notification
{
public function __construct(private string $title, private string $message)
{
}
public function asEmail(): EmailNotification
{
return EmailNotification::create($this->title, $this->message)
->html($this->renderHtml());
}
public function asSms(): SmsNotification
{
return SmsNotification::create($this->message)
->context(['short_code' => 'ORDER']);
}
}
$notification = new BaseNotification('Order Update', 'Your order is shipping!');
$notifier->send($notification->asEmail());
$notifier->send($notification->asSms());
class UserNotification extends Notification
{
public function __construct(private User $user)
{
}
public function asSms(): SmsNotification
{
return SmsNotification::create("Hello {$this->user->name}, your balance is {$this->user->balance}.")
->to($this->user->phone);
}
}
$user = User::find(1);
$notifier->send(new UserNotification($user));
// config/services.php
'notifier' => [
'transports' => [
'sms' => 'sms://default:token@default?from=+1234',
'fallback_sms' => 'sms://backup:token@backup?from=+1234',
],
'fallback_transports' => ['fallback_sms'],
],
config/messenger.php:
'transports' => [
'async' => [
'dsn' => 'sync://',
'options' => [
'retries' => 3,
'delay' => 1000,
],
],
],
WebhookNotification for interactive messages.
use Symfony\Component\Notifier\Notification\WebhookNotification;
class SlackAlert extends Notification
{
public function asSlack(): WebhookNotification
{
return WebhookNotification::create('Deployment Failed', 'Check logs at http://example.com/logs')
->block(new SlackSectionBlock('Error Details', 'Server: prod, Time: ' . now()))
->action(new SlackButtonBlockElement('View Logs', 'http://example.com/logs'));
}
}
$notifier->send(new SlackAlert());
// Event
class OrderShipped implements ShouldBroadcast
{
public function broadcastOn(): array
{
return [new NotificationChannel()];
}
public function toNotifier(array $notifiers, Notification $notification)
{
return $notification->asSms();
}
}
public function boot()
{
Notification::route(
OrderShipped::class,
new NotificationRecipient('sms', '+1234567890')
);
}
$dsn = Dsn::fromString('sms://default:token@default?from=+1234');
.env and validate in AppServiceProvider:
public function boot()
{
$this->validateDsns();
}
protected function validateDsns()
{
$dsn = config('services.notifier.dsn');
if (!$dsn || !Dsn::fromString($dsn)->isValid()) {
throw new \RuntimeException('Invalid Notifier DSN');
}
}
NotificationRecipient for type safety:
$recipient = new NotificationRecipient('sms', '+1234567890'); // Validates E.164 format
$notifier->send($notification->to($recipient));
recipient() method to notifications:
public function recipient(): NotificationRecipient
{
return new NotificationRecipient('sms', $this->user->phone);
}
// config/messenger.php
'defaults' => [
'transport' => 'async',
'dsn' => env('MESSENGER_TRANSPORT_DSN', 'sync://'),
'options' => [
'logger' => true, // Logs all messages
],
],
tap() to inspect notifications:
$notifier->send(
(new SmsNotification('Test'))
->to('+1234567890')
->tap(function ($notification) {
\Log::debug('Sending:', [
'recipient' => $notification->getRecipients()[0],
'content' => $notification->getSubject(),
]);
})
);
| Channel | Gotcha | Solution |
|---|---|---|
| Slack | Blocks exceed 3000 chars | Use SlackSectionBlock::setText() with truncation. |
| Twilio SMS | Whitespace in from number |
Trim and validate: $from = trim($from). |
| HTML emails in plaintext fallback | Use EmailNotification::html() + text(). |
|
| Telegram | Markdown parsing errors | Escape special chars: $message = str_replace(['*', '_'], ['\*', '\_'], $message). |
| Discord | Embed limits (256 chars title) | Split messages or use DiscordEmbedNotification. |
// config/messenger.php
'routing' => [
Notification::class => 'async',
],
How can I help you explore Laravel packages today?