4347 lines
107 KiB
Markdown
4347 lines
107 KiB
Markdown
# Notifications
|
||
|
||
* Introduction
|
||
* Generating Notifications
|
||
* Sending Notifications
|
||
* Using the Notifiable Trait
|
||
* Using the Notification Facade
|
||
* Specifying Delivery Channels
|
||
* Queueing Notifications
|
||
* On-Demand Notifications
|
||
* Mail Notifications
|
||
* Formatting Mail Messages
|
||
* Customizing the Sender
|
||
* Customizing the Recipient
|
||
* Customizing the Subject
|
||
* Customizing the Mailer
|
||
* Customizing the Templates
|
||
* Attachments
|
||
* Adding Tags and Metadata
|
||
* Customizing the Symfony Message
|
||
* Using Mailables
|
||
* Previewing Mail Notifications
|
||
* Markdown Mail Notifications
|
||
* Generating the Message
|
||
* Writing the Message
|
||
* Customizing the Components
|
||
* Database Notifications
|
||
* Prerequisites
|
||
* Formatting Database Notifications
|
||
* Accessing the Notifications
|
||
* Marking Notifications as Read
|
||
* Broadcast Notifications
|
||
* Prerequisites
|
||
* Formatting Broadcast Notifications
|
||
* Listening for Notifications
|
||
* SMS Notifications
|
||
* Prerequisites
|
||
* Formatting SMS Notifications
|
||
* Customizing the "From" Number
|
||
* Adding a Client Reference
|
||
* Routing SMS Notifications
|
||
* Slack Notifications
|
||
* Prerequisites
|
||
* Formatting Slack Notifications
|
||
* Slack Interactivity
|
||
* Routing Slack Notifications
|
||
* Notifying External Slack Workspaces
|
||
* Localizing Notifications
|
||
* Testing
|
||
* Notification Events
|
||
* Custom Channels
|
||
|
||
## Introduction
|
||
|
||
In addition to support for [sending email](/docs/12.x/mail), Laravel provides
|
||
support for sending notifications across a variety of delivery channels,
|
||
including email, SMS (via [Vonage](https://www.vonage.com/communications-
|
||
apis/), formerly known as Nexmo), and [Slack](https://slack.com). In addition,
|
||
a variety of [community built notification channels](https://laravel-
|
||
notification-channels.com/about/#suggesting-a-new-channel) have been created
|
||
to send notifications over dozens of different channels! Notifications may
|
||
also be stored in a database so they may be displayed in your web interface.
|
||
|
||
Typically, notifications should be short, informational messages that notify
|
||
users of something that occurred in your application. For example, if you are
|
||
writing a billing application, you might send an "Invoice Paid" notification
|
||
to your users via the email and SMS channels.
|
||
|
||
## Generating Notifications
|
||
|
||
In Laravel, each notification is represented by a single class that is
|
||
typically stored in the `app/Notifications` directory. Don't worry if you
|
||
don't see this directory in your application - it will be created for you when
|
||
you run the `make:notification` Artisan command:
|
||
|
||
|
||
|
||
1php artisan make:notification InvoicePaid
|
||
|
||
|
||
php artisan make:notification InvoicePaid
|
||
|
||
This command will place a fresh notification class in your `app/Notifications`
|
||
directory. Each notification class contains a `via` method and a variable
|
||
number of message building methods, such as `toMail` or `toDatabase`, that
|
||
convert the notification to a message tailored for that particular channel.
|
||
|
||
## Sending Notifications
|
||
|
||
### Using the Notifiable Trait
|
||
|
||
Notifications may be sent in two ways: using the `notify` method of the
|
||
`Notifiable` trait or using the `Notification` [facade](/docs/12.x/facades).
|
||
The `Notifiable` trait is included on your application's `App\Models\User`
|
||
model by default:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
|
||
6use Illuminate\Notifications\Notifiable;
|
||
|
||
7
|
||
|
||
8class User extends Authenticatable
|
||
|
||
9{
|
||
|
||
10 use Notifiable;
|
||
|
||
11}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use Notifiable;
|
||
}
|
||
|
||
The `notify` method that is provided by this trait expects to receive a
|
||
notification instance:
|
||
|
||
|
||
|
||
1use App\Notifications\InvoicePaid;
|
||
|
||
2
|
||
|
||
3$user->notify(new InvoicePaid($invoice));
|
||
|
||
|
||
use App\Notifications\InvoicePaid;
|
||
|
||
$user->notify(new InvoicePaid($invoice));
|
||
|
||
Remember, you may use the `Notifiable` trait on any of your models. You are
|
||
not limited to only including it on your `User` model.
|
||
|
||
### Using the Notification Facade
|
||
|
||
Alternatively, you may send notifications via the `Notification`
|
||
[facade](/docs/12.x/facades). This approach is useful when you need to send a
|
||
notification to multiple notifiable entities such as a collection of users. To
|
||
send notifications using the facade, pass all of the notifiable entities and
|
||
the notification instance to the `send` method:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Notification;
|
||
|
||
2
|
||
|
||
3Notification::send($users, new InvoicePaid($invoice));
|
||
|
||
|
||
use Illuminate\Support\Facades\Notification;
|
||
|
||
Notification::send($users, new InvoicePaid($invoice));
|
||
|
||
You can also send notifications immediately using the `sendNow` method. This
|
||
method will send the notification immediately even if the notification
|
||
implements the `ShouldQueue` interface:
|
||
|
||
|
||
|
||
1Notification::sendNow($developers, new DeploymentCompleted($deployment));
|
||
|
||
|
||
Notification::sendNow($developers, new DeploymentCompleted($deployment));
|
||
|
||
### Specifying Delivery Channels
|
||
|
||
Every notification class has a `via` method that determines on which channels
|
||
the notification will be delivered. Notifications may be sent on the `mail`,
|
||
`database`, `broadcast`, `vonage`, and `slack` channels.
|
||
|
||
If you would like to use other delivery channels such as Telegram or Pusher,
|
||
check out the community driven [Laravel Notification Channels
|
||
website](http://laravel-notification-channels.com).
|
||
|
||
The `via` method receives a `$notifiable` instance, which will be an instance
|
||
of the class to which the notification is being sent. You may use
|
||
`$notifiable` to determine which channels the notification should be delivered
|
||
on:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the notification's delivery channels.
|
||
|
||
3 *
|
||
|
||
4 * @return array<int, string>
|
||
|
||
5 */
|
||
|
||
6public function via(object $notifiable): array
|
||
|
||
7{
|
||
|
||
8 return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the notification's delivery channels.
|
||
*
|
||
* @return array<int, string>
|
||
*/
|
||
public function via(object $notifiable): array
|
||
{
|
||
return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];
|
||
}
|
||
|
||
### Queueing Notifications
|
||
|
||
Before queueing notifications, you should configure your queue and [start a
|
||
worker](/docs/12.x/queues#running-the-queue-worker).
|
||
|
||
Sending notifications can take time, especially if the channel needs to make
|
||
an external API call to deliver the notification. To speed up your
|
||
application's response time, let your notification be queued by adding the
|
||
`ShouldQueue` interface and `Queueable` trait to your class. The interface and
|
||
trait are already imported for all notifications generated using the
|
||
`make:notification` command, so you may immediately add them to your
|
||
notification class:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Notifications;
|
||
|
||
4
|
||
|
||
5use Illuminate\Bus\Queueable;
|
||
|
||
6use Illuminate\Contracts\Queue\ShouldQueue;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8
|
||
|
||
9class InvoicePaid extends Notification implements ShouldQueue
|
||
|
||
10{
|
||
|
||
11 use Queueable;
|
||
|
||
12
|
||
|
||
13 // ...
|
||
|
||
14}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Notifications;
|
||
|
||
use Illuminate\Bus\Queueable;
|
||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class InvoicePaid extends Notification implements ShouldQueue
|
||
{
|
||
use Queueable;
|
||
|
||
// ...
|
||
}
|
||
|
||
Once the `ShouldQueue` interface has been added to your notification, you may
|
||
send the notification like normal. Laravel will detect the `ShouldQueue`
|
||
interface on the class and automatically queue the delivery of the
|
||
notification:
|
||
|
||
|
||
|
||
1$user->notify(new InvoicePaid($invoice));
|
||
|
||
|
||
$user->notify(new InvoicePaid($invoice));
|
||
|
||
When queueing notifications, a queued job will be created for each recipient
|
||
and channel combination. For example, six jobs will be dispatched to the queue
|
||
if your notification has three recipients and two channels.
|
||
|
||
#### Delaying Notifications
|
||
|
||
If you would like to delay the delivery of the notification, you may chain the
|
||
`delay` method onto your notification instantiation:
|
||
|
||
|
||
|
||
1$delay = now()->addMinutes(10);
|
||
|
||
2
|
||
|
||
3$user->notify((new InvoicePaid($invoice))->delay($delay));
|
||
|
||
|
||
$delay = now()->addMinutes(10);
|
||
|
||
$user->notify((new InvoicePaid($invoice))->delay($delay));
|
||
|
||
You may pass an array to the `delay` method to specify the delay amount for
|
||
specific channels:
|
||
|
||
|
||
|
||
1$user->notify((new InvoicePaid($invoice))->delay([
|
||
|
||
2 'mail' => now()->addMinutes(5),
|
||
|
||
3 'sms' => now()->addMinutes(10),
|
||
|
||
4]));
|
||
|
||
|
||
$user->notify((new InvoicePaid($invoice))->delay([
|
||
'mail' => now()->addMinutes(5),
|
||
'sms' => now()->addMinutes(10),
|
||
]));
|
||
|
||
Alternatively, you may define a `withDelay` method on the notification class
|
||
itself. The `withDelay` method should return an array of channel names and
|
||
delay values:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Determine the notification's delivery delay.
|
||
|
||
3 *
|
||
|
||
4 * @return array<string, \Illuminate\Support\Carbon>
|
||
|
||
5 */
|
||
|
||
6public function withDelay(object $notifiable): array
|
||
|
||
7{
|
||
|
||
8 return [
|
||
|
||
9 'mail' => now()->addMinutes(5),
|
||
|
||
10 'sms' => now()->addMinutes(10),
|
||
|
||
11 ];
|
||
|
||
12}
|
||
|
||
|
||
/**
|
||
* Determine the notification's delivery delay.
|
||
*
|
||
* @return array<string, \Illuminate\Support\Carbon>
|
||
*/
|
||
public function withDelay(object $notifiable): array
|
||
{
|
||
return [
|
||
'mail' => now()->addMinutes(5),
|
||
'sms' => now()->addMinutes(10),
|
||
];
|
||
}
|
||
|
||
#### Customizing the Notification Queue Connection
|
||
|
||
By default, queued notifications will be queued using your application's
|
||
default queue connection. If you would like to specify a different connection
|
||
that should be used for a particular notification, you may call the
|
||
`onConnection` method from your notification's constructor:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Notifications;
|
||
|
||
4
|
||
|
||
5use Illuminate\Bus\Queueable;
|
||
|
||
6use Illuminate\Contracts\Queue\ShouldQueue;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8
|
||
|
||
9class InvoicePaid extends Notification implements ShouldQueue
|
||
|
||
10{
|
||
|
||
11 use Queueable;
|
||
|
||
12
|
||
|
||
13 /**
|
||
|
||
14 * Create a new notification instance.
|
||
|
||
15 */
|
||
|
||
16 public function __construct()
|
||
|
||
17 {
|
||
|
||
18 $this->onConnection('redis');
|
||
|
||
19 }
|
||
|
||
20}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Notifications;
|
||
|
||
use Illuminate\Bus\Queueable;
|
||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class InvoicePaid extends Notification implements ShouldQueue
|
||
{
|
||
use Queueable;
|
||
|
||
/**
|
||
* Create a new notification instance.
|
||
*/
|
||
public function __construct()
|
||
{
|
||
$this->onConnection('redis');
|
||
}
|
||
}
|
||
|
||
Or, if you would like to specify a specific queue connection that should be
|
||
used for each notification channel supported by the notification, you may
|
||
define a `viaConnections` method on your notification. This method should
|
||
return an array of channel name / queue connection name pairs:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Determine which connections should be used for each notification channel.
|
||
|
||
3 *
|
||
|
||
4 * @return array<string, string>
|
||
|
||
5 */
|
||
|
||
6public function viaConnections(): array
|
||
|
||
7{
|
||
|
||
8 return [
|
||
|
||
9 'mail' => 'redis',
|
||
|
||
10 'database' => 'sync',
|
||
|
||
11 ];
|
||
|
||
12}
|
||
|
||
|
||
/**
|
||
* Determine which connections should be used for each notification channel.
|
||
*
|
||
* @return array<string, string>
|
||
*/
|
||
public function viaConnections(): array
|
||
{
|
||
return [
|
||
'mail' => 'redis',
|
||
'database' => 'sync',
|
||
];
|
||
}
|
||
|
||
#### Customizing Notification Channel Queues
|
||
|
||
If you would like to specify a specific queue that should be used for each
|
||
notification channel supported by the notification, you may define a
|
||
`viaQueues` method on your notification. This method should return an array of
|
||
channel name / queue name pairs:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Determine which queues should be used for each notification channel.
|
||
|
||
3 *
|
||
|
||
4 * @return array<string, string>
|
||
|
||
5 */
|
||
|
||
6public function viaQueues(): array
|
||
|
||
7{
|
||
|
||
8 return [
|
||
|
||
9 'mail' => 'mail-queue',
|
||
|
||
10 'slack' => 'slack-queue',
|
||
|
||
11 ];
|
||
|
||
12}
|
||
|
||
|
||
/**
|
||
* Determine which queues should be used for each notification channel.
|
||
*
|
||
* @return array<string, string>
|
||
*/
|
||
public function viaQueues(): array
|
||
{
|
||
return [
|
||
'mail' => 'mail-queue',
|
||
'slack' => 'slack-queue',
|
||
];
|
||
}
|
||
|
||
#### Queued Notification Middleware
|
||
|
||
Queued notifications may define middleware [just like queued
|
||
jobs](/docs/12.x/queues#job-middleware). To get started, define a `middleware`
|
||
method on your notification class. The `middleware` method will receive
|
||
`$notifiable` and `$channel` variables, which allow you to customize the
|
||
returned middleware based on the notification's destination:
|
||
|
||
|
||
|
||
1use Illuminate\Queue\Middleware\RateLimited;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the middleware the notification job should pass through.
|
||
|
||
5 *
|
||
|
||
6 * @return array<int, object>
|
||
|
||
7 */
|
||
|
||
8public function middleware(object $notifiable, string $channel)
|
||
|
||
9{
|
||
|
||
10 return match ($channel) {
|
||
|
||
11 'mail' => [new RateLimited('postmark')],
|
||
|
||
12 'slack' => [new RateLimited('slack')],
|
||
|
||
13 default => [],
|
||
|
||
14 };
|
||
|
||
15}
|
||
|
||
|
||
use Illuminate\Queue\Middleware\RateLimited;
|
||
|
||
/**
|
||
* Get the middleware the notification job should pass through.
|
||
*
|
||
* @return array<int, object>
|
||
*/
|
||
public function middleware(object $notifiable, string $channel)
|
||
{
|
||
return match ($channel) {
|
||
'mail' => [new RateLimited('postmark')],
|
||
'slack' => [new RateLimited('slack')],
|
||
default => [],
|
||
};
|
||
}
|
||
|
||
#### Queued Notifications and Database Transactions
|
||
|
||
When queued notifications are dispatched within database transactions, they
|
||
may be processed by the queue before the database transaction has committed.
|
||
When this happens, any updates you have made to models or database records
|
||
during the database transaction may not yet be reflected in the database. In
|
||
addition, any models or database records created within the transaction may
|
||
not exist in the database. If your notification depends on these models,
|
||
unexpected errors can occur when the job that sends the queued notification is
|
||
processed.
|
||
|
||
If your queue connection's `after_commit` configuration option is set to
|
||
`false`, you may still indicate that a particular queued notification should
|
||
be dispatched after all open database transactions have been committed by
|
||
calling the `afterCommit` method when sending the notification:
|
||
|
||
|
||
|
||
1use App\Notifications\InvoicePaid;
|
||
|
||
2
|
||
|
||
3$user->notify((new InvoicePaid($invoice))->afterCommit());
|
||
|
||
|
||
use App\Notifications\InvoicePaid;
|
||
|
||
$user->notify((new InvoicePaid($invoice))->afterCommit());
|
||
|
||
Alternatively, you may call the `afterCommit` method from your notification's
|
||
constructor:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Notifications;
|
||
|
||
4
|
||
|
||
5use Illuminate\Bus\Queueable;
|
||
|
||
6use Illuminate\Contracts\Queue\ShouldQueue;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8
|
||
|
||
9class InvoicePaid extends Notification implements ShouldQueue
|
||
|
||
10{
|
||
|
||
11 use Queueable;
|
||
|
||
12
|
||
|
||
13 /**
|
||
|
||
14 * Create a new notification instance.
|
||
|
||
15 */
|
||
|
||
16 public function __construct()
|
||
|
||
17 {
|
||
|
||
18 $this->afterCommit();
|
||
|
||
19 }
|
||
|
||
20}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Notifications;
|
||
|
||
use Illuminate\Bus\Queueable;
|
||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class InvoicePaid extends Notification implements ShouldQueue
|
||
{
|
||
use Queueable;
|
||
|
||
/**
|
||
* Create a new notification instance.
|
||
*/
|
||
public function __construct()
|
||
{
|
||
$this->afterCommit();
|
||
}
|
||
}
|
||
|
||
To learn more about working around these issues, please review the
|
||
documentation regarding [queued jobs and database
|
||
transactions](/docs/12.x/queues#jobs-and-database-transactions).
|
||
|
||
#### Determining if a Queued Notification Should Be Sent
|
||
|
||
After a queued notification has been dispatched for the queue for background
|
||
processing, it will typically be accepted by a queue worker and sent to its
|
||
intended recipient.
|
||
|
||
However, if you would like to make the final determination on whether the
|
||
queued notification should be sent after it is being processed by a queue
|
||
worker, you may define a `shouldSend` method on the notification class. If
|
||
this method returns `false`, the notification will not be sent:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Determine if the notification should be sent.
|
||
|
||
3 */
|
||
|
||
4public function shouldSend(object $notifiable, string $channel): bool
|
||
|
||
5{
|
||
|
||
6 return $this->invoice->isPaid();
|
||
|
||
7}
|
||
|
||
|
||
/**
|
||
* Determine if the notification should be sent.
|
||
*/
|
||
public function shouldSend(object $notifiable, string $channel): bool
|
||
{
|
||
return $this->invoice->isPaid();
|
||
}
|
||
|
||
### On-Demand Notifications
|
||
|
||
Sometimes you may need to send a notification to someone who is not stored as
|
||
a "user" of your application. Using the `Notification` facade's `route`
|
||
method, you may specify ad-hoc notification routing information before sending
|
||
the notification:
|
||
|
||
|
||
|
||
1use Illuminate\Broadcasting\Channel;
|
||
|
||
2use Illuminate\Support\Facades\Notification;
|
||
|
||
3
|
||
|
||
4Notification::route('mail', '[[email protected]](/cdn-cgi/l/email-protection)')
|
||
|
||
5 ->route('vonage', '5555555555')
|
||
|
||
6 ->route('slack', '#slack-channel')
|
||
|
||
7 ->route('broadcast', [new Channel('channel-name')])
|
||
|
||
8 ->notify(new InvoicePaid($invoice));
|
||
|
||
|
||
use Illuminate\Broadcasting\Channel;
|
||
use Illuminate\Support\Facades\Notification;
|
||
|
||
Notification::route('mail', '[[email protected]](/cdn-cgi/l/email-protection)')
|
||
->route('vonage', '5555555555')
|
||
->route('slack', '#slack-channel')
|
||
->route('broadcast', [new Channel('channel-name')])
|
||
->notify(new InvoicePaid($invoice));
|
||
|
||
If you would like to provide the recipient's name when sending an on-demand
|
||
notification to the `mail` route, you may provide an array that contains the
|
||
email address as the key and the name as the value of the first element in the
|
||
array:
|
||
|
||
|
||
|
||
1Notification::route('mail', [
|
||
|
||
2 '[[email protected]](/cdn-cgi/l/email-protection)' => 'Barrett Blair',
|
||
|
||
3])->notify(new InvoicePaid($invoice));
|
||
|
||
|
||
Notification::route('mail', [
|
||
'[[email protected]](/cdn-cgi/l/email-protection)' => 'Barrett Blair',
|
||
])->notify(new InvoicePaid($invoice));
|
||
|
||
Using the `routes` method, you may provide ad-hoc routing information for
|
||
multiple notification channels at once:
|
||
|
||
|
||
|
||
1Notification::routes([
|
||
|
||
2 'mail' => ['[[email protected]](/cdn-cgi/l/email-protection)' => 'Barrett Blair'],
|
||
|
||
3 'vonage' => '5555555555',
|
||
|
||
4])->notify(new InvoicePaid($invoice));
|
||
|
||
|
||
Notification::routes([
|
||
'mail' => ['[[email protected]](/cdn-cgi/l/email-protection)' => 'Barrett Blair'],
|
||
'vonage' => '5555555555',
|
||
])->notify(new InvoicePaid($invoice));
|
||
|
||
## Mail Notifications
|
||
|
||
### Formatting Mail Messages
|
||
|
||
If a notification supports being sent as an email, you should define a
|
||
`toMail` method on the notification class. This method will receive a
|
||
`$notifiable` entity and should return an
|
||
`Illuminate\Notifications\Messages\MailMessage` instance.
|
||
|
||
The `MailMessage` class contains a few simple methods to help you build
|
||
transactional email messages. Mail messages may contain lines of text as well
|
||
as a "call to action". Let's take a look at an example `toMail` method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 $url = url('/invoice/'.$this->invoice->id);
|
||
|
||
7
|
||
|
||
8 return (new MailMessage)
|
||
|
||
9 ->greeting('Hello!')
|
||
|
||
10 ->line('One of your invoices has been paid!')
|
||
|
||
11 ->lineIf($this->amount > 0, "Amount paid: {$this->amount}")
|
||
|
||
12 ->action('View Invoice', $url)
|
||
|
||
13 ->line('Thank you for using our application!');
|
||
|
||
14}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
$url = url('/invoice/'.$this->invoice->id);
|
||
|
||
return (new MailMessage)
|
||
->greeting('Hello!')
|
||
->line('One of your invoices has been paid!')
|
||
->lineIf($this->amount > 0, "Amount paid: {$this->amount}")
|
||
->action('View Invoice', $url)
|
||
->line('Thank you for using our application!');
|
||
}
|
||
|
||
Note we are using `$this->invoice->id` in our `toMail` method. You may pass
|
||
any data your notification needs to generate its message into the
|
||
notification's constructor.
|
||
|
||
In this example, we register a greeting, a line of text, a call to action, and
|
||
then another line of text. These methods provided by the `MailMessage` object
|
||
make it simple and fast to format small transactional emails. The mail channel
|
||
will then translate the message components into a beautiful, responsive HTML
|
||
email template with a plain-text counterpart. Here is an example of an email
|
||
generated by the `mail` channel:
|
||
|
||

|
||
|
||
When sending mail notifications, be sure to set the `name` configuration
|
||
option in your `config/app.php` configuration file. This value will be used in
|
||
the header and footer of your mail notification messages.
|
||
|
||
#### Error Messages
|
||
|
||
Some notifications inform users of errors, such as a failed invoice payment.
|
||
You may indicate that a mail message is regarding an error by calling the
|
||
`error` method when building your message. When using the `error` method on a
|
||
mail message, the call to action button will be red instead of black:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->error()
|
||
|
||
8 ->subject('Invoice Payment Failed')
|
||
|
||
9 ->line('...');
|
||
|
||
10}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->error()
|
||
->subject('Invoice Payment Failed')
|
||
->line('...');
|
||
}
|
||
|
||
#### Other Mail Notification Formatting Options
|
||
|
||
Instead of defining the "lines" of text in the notification class, you may use
|
||
the `view` method to specify a custom template that should be used to render
|
||
the notification email:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)->view(
|
||
|
||
7 'mail.invoice.paid', ['invoice' => $this->invoice]
|
||
|
||
8 );
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)->view(
|
||
'mail.invoice.paid', ['invoice' => $this->invoice]
|
||
);
|
||
}
|
||
|
||
You may specify a plain-text view for the mail message by passing the view
|
||
name as the second element of an array that is given to the `view` method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)->view(
|
||
|
||
7 ['mail.invoice.paid', 'mail.invoice.paid-text'],
|
||
|
||
8 ['invoice' => $this->invoice]
|
||
|
||
9 );
|
||
|
||
10}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)->view(
|
||
['mail.invoice.paid', 'mail.invoice.paid-text'],
|
||
['invoice' => $this->invoice]
|
||
);
|
||
}
|
||
|
||
Or, if your message only has a plain-text view, you may utilize the `text`
|
||
method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)->text(
|
||
|
||
7 'mail.invoice.paid-text', ['invoice' => $this->invoice]
|
||
|
||
8 );
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)->text(
|
||
'mail.invoice.paid-text', ['invoice' => $this->invoice]
|
||
);
|
||
}
|
||
|
||
### Customizing the Sender
|
||
|
||
By default, the email's sender / from address is defined in the
|
||
`config/mail.php` configuration file. However, you may specify the from
|
||
address for a specific notification using the `from` method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->from('[[email protected]](/cdn-cgi/l/email-protection)', 'Barrett Blair')
|
||
|
||
8 ->line('...');
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->from('[[email protected]](/cdn-cgi/l/email-protection)', 'Barrett Blair')
|
||
->line('...');
|
||
}
|
||
|
||
### Customizing the Recipient
|
||
|
||
When sending notifications via the `mail` channel, the notification system
|
||
will automatically look for an `email` property on your notifiable entity. You
|
||
may customize which email address is used to deliver the notification by
|
||
defining a `routeNotificationForMail` method on the notifiable entity:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
|
||
6use Illuminate\Notifications\Notifiable;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8
|
||
|
||
9class User extends Authenticatable
|
||
|
||
10{
|
||
|
||
11 use Notifiable;
|
||
|
||
12
|
||
|
||
13 /**
|
||
|
||
14 * Route notifications for the mail channel.
|
||
|
||
15 *
|
||
|
||
16 * @return array<string, string>|string
|
||
|
||
17 */
|
||
|
||
18 public function routeNotificationForMail(Notification $notification): array|string
|
||
|
||
19 {
|
||
|
||
20 // Return email address only...
|
||
|
||
21 return $this->email_address;
|
||
|
||
22
|
||
|
||
23 // Return email address and name...
|
||
|
||
24 return [$this->email_address => $this->name];
|
||
|
||
25 }
|
||
|
||
26}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use Notifiable;
|
||
|
||
/**
|
||
* Route notifications for the mail channel.
|
||
*
|
||
* @return array<string, string>|string
|
||
*/
|
||
public function routeNotificationForMail(Notification $notification): array|string
|
||
{
|
||
// Return email address only...
|
||
return $this->email_address;
|
||
|
||
// Return email address and name...
|
||
return [$this->email_address => $this->name];
|
||
}
|
||
}
|
||
|
||
### Customizing the Subject
|
||
|
||
By default, the email's subject is the class name of the notification
|
||
formatted to "Title Case". So, if your notification class is named
|
||
`InvoicePaid`, the email's subject will be `Invoice Paid`. If you would like
|
||
to specify a different subject for the message, you may call the `subject`
|
||
method when building your message:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->subject('Notification Subject')
|
||
|
||
8 ->line('...');
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->subject('Notification Subject')
|
||
->line('...');
|
||
}
|
||
|
||
### Customizing the Mailer
|
||
|
||
By default, the email notification will be sent using the default mailer
|
||
defined in the `config/mail.php` configuration file. However, you may specify
|
||
a different mailer at runtime by calling the `mailer` method when building
|
||
your message:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->mailer('postmark')
|
||
|
||
8 ->line('...');
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->mailer('postmark')
|
||
->line('...');
|
||
}
|
||
|
||
### Customizing the Templates
|
||
|
||
You can modify the HTML and plain-text template used by mail notifications by
|
||
publishing the notification package's resources. After running this command,
|
||
the mail notification templates will be located in the
|
||
`resources/views/vendor/notifications` directory:
|
||
|
||
|
||
|
||
1php artisan vendor:publish --tag=laravel-notifications
|
||
|
||
|
||
php artisan vendor:publish --tag=laravel-notifications
|
||
|
||
### Attachments
|
||
|
||
To add attachments to an email notification, use the `attach` method while
|
||
building your message. The `attach` method accepts the absolute path to the
|
||
file as its first argument:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->greeting('Hello!')
|
||
|
||
8 ->attach('/path/to/file');
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->greeting('Hello!')
|
||
->attach('/path/to/file');
|
||
}
|
||
|
||
The `attach` method offered by notification mail messages also accepts
|
||
[attachable objects](/docs/12.x/mail#attachable-objects). Please consult the
|
||
comprehensive [attachable object documentation](/docs/12.x/mail#attachable-
|
||
objects) to learn more.
|
||
|
||
When attaching files to a message, you may also specify the display name and /
|
||
or MIME type by passing an `array` as the second argument to the `attach`
|
||
method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->greeting('Hello!')
|
||
|
||
8 ->attach('/path/to/file', [
|
||
|
||
9 'as' => 'name.pdf',
|
||
|
||
10 'mime' => 'application/pdf',
|
||
|
||
11 ]);
|
||
|
||
12}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->greeting('Hello!')
|
||
->attach('/path/to/file', [
|
||
'as' => 'name.pdf',
|
||
'mime' => 'application/pdf',
|
||
]);
|
||
}
|
||
|
||
Unlike attaching files in mailable objects, you may not attach a file directly
|
||
from a storage disk using `attachFromStorage`. You should rather use the
|
||
`attach` method with an absolute path to the file on the storage disk.
|
||
Alternatively, you could return a [mailable](/docs/12.x/mail#generating-
|
||
mailables) from the `toMail` method:
|
||
|
||
|
||
|
||
1use App\Mail\InvoicePaid as InvoicePaidMailable;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the mail representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toMail(object $notifiable): Mailable
|
||
|
||
7{
|
||
|
||
8 return (new InvoicePaidMailable($this->invoice))
|
||
|
||
9 ->to($notifiable->email)
|
||
|
||
10 ->attachFromStorage('/path/to/file');
|
||
|
||
11}
|
||
|
||
|
||
use App\Mail\InvoicePaid as InvoicePaidMailable;
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): Mailable
|
||
{
|
||
return (new InvoicePaidMailable($this->invoice))
|
||
->to($notifiable->email)
|
||
->attachFromStorage('/path/to/file');
|
||
}
|
||
|
||
When necessary, multiple files may be attached to a message using the
|
||
`attachMany` method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->greeting('Hello!')
|
||
|
||
8 ->attachMany([
|
||
|
||
9 '/path/to/forge.svg',
|
||
|
||
10 '/path/to/vapor.svg' => [
|
||
|
||
11 'as' => 'Logo.svg',
|
||
|
||
12 'mime' => 'image/svg+xml',
|
||
|
||
13 ],
|
||
|
||
14 ]);
|
||
|
||
15}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->greeting('Hello!')
|
||
->attachMany([
|
||
'/path/to/forge.svg',
|
||
'/path/to/vapor.svg' => [
|
||
'as' => 'Logo.svg',
|
||
'mime' => 'image/svg+xml',
|
||
],
|
||
]);
|
||
}
|
||
|
||
#### Raw Data Attachments
|
||
|
||
The `attachData` method may be used to attach a raw string of bytes as an
|
||
attachment. When calling the `attachData` method, you should provide the
|
||
filename that should be assigned to the attachment:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->greeting('Hello!')
|
||
|
||
8 ->attachData($this->pdf, 'name.pdf', [
|
||
|
||
9 'mime' => 'application/pdf',
|
||
|
||
10 ]);
|
||
|
||
11}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->greeting('Hello!')
|
||
->attachData($this->pdf, 'name.pdf', [
|
||
'mime' => 'application/pdf',
|
||
]);
|
||
}
|
||
|
||
### Adding Tags and Metadata
|
||
|
||
Some third-party email providers such as Mailgun and Postmark support message
|
||
"tags" and "metadata", which may be used to group and track emails sent by
|
||
your application. You may add tags and metadata to an email message via the
|
||
`tag` and `metadata` methods:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->greeting('Comment Upvoted!')
|
||
|
||
8 ->tag('upvote')
|
||
|
||
9 ->metadata('comment_id', $this->comment->id);
|
||
|
||
10}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->greeting('Comment Upvoted!')
|
||
->tag('upvote')
|
||
->metadata('comment_id', $this->comment->id);
|
||
}
|
||
|
||
If your application is using the Mailgun driver, you may consult Mailgun's
|
||
documentation for more information on
|
||
[tags](https://documentation.mailgun.com/docs/mailgun/user-manual/tracking-
|
||
messages/#tags) and
|
||
[metadata](https://documentation.mailgun.com/docs/mailgun/user-manual/sending-
|
||
messages/#attaching-metadata-to-messages). Likewise, the Postmark
|
||
documentation may also be consulted for more information on their support for
|
||
[tags](https://postmarkapp.com/blog/tags-support-for-smtp) and
|
||
[metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq).
|
||
|
||
If your application is using Amazon SES to send emails, you should use the
|
||
`metadata` method to attach [SES
|
||
"tags"](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html)
|
||
to the message.
|
||
|
||
### Customizing the Symfony Message
|
||
|
||
The `withSymfonyMessage` method of the `MailMessage` class allows you to
|
||
register a closure which will be invoked with the Symfony Message instance
|
||
before sending the message. This gives you an opportunity to deeply customize
|
||
the message before it is delivered:
|
||
|
||
|
||
|
||
1use Symfony\Component\Mime\Email;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the mail representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toMail(object $notifiable): MailMessage
|
||
|
||
7{
|
||
|
||
8 return (new MailMessage)
|
||
|
||
9 ->withSymfonyMessage(function (Email $message) {
|
||
|
||
10 $message->getHeaders()->addTextHeader(
|
||
|
||
11 'Custom-Header', 'Header Value'
|
||
|
||
12 );
|
||
|
||
13 });
|
||
|
||
14}
|
||
|
||
|
||
use Symfony\Component\Mime\Email;
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->withSymfonyMessage(function (Email $message) {
|
||
$message->getHeaders()->addTextHeader(
|
||
'Custom-Header', 'Header Value'
|
||
);
|
||
});
|
||
}
|
||
|
||
### Using Mailables
|
||
|
||
If needed, you may return a full [mailable object](/docs/12.x/mail) from your
|
||
notification's `toMail` method. When returning a `Mailable` instead of a
|
||
`MailMessage`, you will need to specify the message recipient using the
|
||
mailable object's `to` method:
|
||
|
||
|
||
|
||
1use App\Mail\InvoicePaid as InvoicePaidMailable;
|
||
|
||
2use Illuminate\Mail\Mailable;
|
||
|
||
3
|
||
|
||
4/**
|
||
|
||
5 * Get the mail representation of the notification.
|
||
|
||
6 */
|
||
|
||
7public function toMail(object $notifiable): Mailable
|
||
|
||
8{
|
||
|
||
9 return (new InvoicePaidMailable($this->invoice))
|
||
|
||
10 ->to($notifiable->email);
|
||
|
||
11}
|
||
|
||
|
||
use App\Mail\InvoicePaid as InvoicePaidMailable;
|
||
use Illuminate\Mail\Mailable;
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): Mailable
|
||
{
|
||
return (new InvoicePaidMailable($this->invoice))
|
||
->to($notifiable->email);
|
||
}
|
||
|
||
#### Mailables and On-Demand Notifications
|
||
|
||
If you are sending an on-demand notification, the `$notifiable` instance given
|
||
to the `toMail` method will be an instance of
|
||
`Illuminate\Notifications\AnonymousNotifiable`, which offers a
|
||
`routeNotificationFor` method that may be used to retrieve the email address
|
||
the on-demand notification should be sent to:
|
||
|
||
|
||
|
||
1use App\Mail\InvoicePaid as InvoicePaidMailable;
|
||
|
||
2use Illuminate\Notifications\AnonymousNotifiable;
|
||
|
||
3use Illuminate\Mail\Mailable;
|
||
|
||
4
|
||
|
||
5/**
|
||
|
||
6 * Get the mail representation of the notification.
|
||
|
||
7 */
|
||
|
||
8public function toMail(object $notifiable): Mailable
|
||
|
||
9{
|
||
|
||
10 $address = $notifiable instanceof AnonymousNotifiable
|
||
|
||
11 ? $notifiable->routeNotificationFor('mail')
|
||
|
||
12 : $notifiable->email;
|
||
|
||
13
|
||
|
||
14 return (new InvoicePaidMailable($this->invoice))
|
||
|
||
15 ->to($address);
|
||
|
||
16}
|
||
|
||
|
||
use App\Mail\InvoicePaid as InvoicePaidMailable;
|
||
use Illuminate\Notifications\AnonymousNotifiable;
|
||
use Illuminate\Mail\Mailable;
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): Mailable
|
||
{
|
||
$address = $notifiable instanceof AnonymousNotifiable
|
||
? $notifiable->routeNotificationFor('mail')
|
||
: $notifiable->email;
|
||
|
||
return (new InvoicePaidMailable($this->invoice))
|
||
->to($address);
|
||
}
|
||
|
||
### Previewing Mail Notifications
|
||
|
||
When designing a mail notification template, it is convenient to quickly
|
||
preview the rendered mail message in your browser like a typical Blade
|
||
template. For this reason, Laravel allows you to return any mail message
|
||
generated by a mail notification directly from a route closure or controller.
|
||
When a `MailMessage` is returned, it will be rendered and displayed in the
|
||
browser, allowing you to quickly preview its design without needing to send it
|
||
to an actual email address:
|
||
|
||
|
||
|
||
1use App\Models\Invoice;
|
||
|
||
2use App\Notifications\InvoicePaid;
|
||
|
||
3
|
||
|
||
4Route::get('/notification', function () {
|
||
|
||
5 $invoice = Invoice::find(1);
|
||
|
||
6
|
||
|
||
7 return (new InvoicePaid($invoice))
|
||
|
||
8 ->toMail($invoice->user);
|
||
|
||
9});
|
||
|
||
|
||
use App\Models\Invoice;
|
||
use App\Notifications\InvoicePaid;
|
||
|
||
Route::get('/notification', function () {
|
||
$invoice = Invoice::find(1);
|
||
|
||
return (new InvoicePaid($invoice))
|
||
->toMail($invoice->user);
|
||
});
|
||
|
||
## Markdown Mail Notifications
|
||
|
||
Markdown mail notifications allow you to take advantage of the pre-built
|
||
templates of mail notifications, while giving you more freedom to write
|
||
longer, customized messages. Since the messages are written in Markdown,
|
||
Laravel is able to render beautiful, responsive HTML templates for the
|
||
messages while also automatically generating a plain-text counterpart.
|
||
|
||
### Generating the Message
|
||
|
||
To generate a notification with a corresponding Markdown template, you may use
|
||
the `--markdown` option of the `make:notification` Artisan command:
|
||
|
||
|
||
|
||
1php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
|
||
|
||
|
||
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
|
||
|
||
Like all other mail notifications, notifications that use Markdown templates
|
||
should define a `toMail` method on their notification class. However, instead
|
||
of using the `line` and `action` methods to construct the notification, use
|
||
the `markdown` method to specify the name of the Markdown template that should
|
||
be used. An array of data you wish to make available to the template may be
|
||
passed as the method's second argument:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 $url = url('/invoice/'.$this->invoice->id);
|
||
|
||
7
|
||
|
||
8 return (new MailMessage)
|
||
|
||
9 ->subject('Invoice Paid')
|
||
|
||
10 ->markdown('mail.invoice.paid', ['url' => $url]);
|
||
|
||
11}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
$url = url('/invoice/'.$this->invoice->id);
|
||
|
||
return (new MailMessage)
|
||
->subject('Invoice Paid')
|
||
->markdown('mail.invoice.paid', ['url' => $url]);
|
||
}
|
||
|
||
### Writing the Message
|
||
|
||
Markdown mail notifications use a combination of Blade components and Markdown
|
||
syntax which allow you to easily construct notifications while leveraging
|
||
Laravel's pre-crafted notification components:
|
||
|
||
|
||
|
||
1<x-mail::message>
|
||
|
||
2# Invoice Paid
|
||
|
||
3
|
||
|
||
4Your invoice has been paid!
|
||
|
||
5
|
||
|
||
6<x-mail::button :url="$url">
|
||
|
||
7View Invoice
|
||
|
||
8</x-mail::button>
|
||
|
||
9
|
||
|
||
10Thanks,<br>
|
||
|
||
11{{ config('app.name') }}
|
||
|
||
12</x-mail::message>
|
||
|
||
|
||
<x-mail::message>
|
||
# Invoice Paid
|
||
|
||
Your invoice has been paid!
|
||
|
||
<x-mail::button :url="$url">
|
||
View Invoice
|
||
</x-mail::button>
|
||
|
||
Thanks,<br>
|
||
{{ config('app.name') }}
|
||
</x-mail::message>
|
||
|
||
Do not use excess indentation when writing Markdown emails. Per Markdown
|
||
standards, Markdown parsers will render indented content as code blocks.
|
||
|
||
#### Button Component
|
||
|
||
The button component renders a centered button link. The component accepts two
|
||
arguments, a `url` and an optional `color`. Supported colors are `primary`,
|
||
`green`, and `red`. You may add as many button components to a notification as
|
||
you wish:
|
||
|
||
|
||
|
||
1<x-mail::button :url="$url" color="green">
|
||
|
||
2View Invoice
|
||
|
||
3</x-mail::button>
|
||
|
||
|
||
<x-mail::button :url="$url" color="green">
|
||
View Invoice
|
||
</x-mail::button>
|
||
|
||
#### Panel Component
|
||
|
||
The panel component renders the given block of text in a panel that has a
|
||
slightly different background color than the rest of the notification. This
|
||
allows you to draw attention to a given block of text:
|
||
|
||
|
||
|
||
1<x-mail::panel>
|
||
|
||
2This is the panel content.
|
||
|
||
3</x-mail::panel>
|
||
|
||
|
||
<x-mail::panel>
|
||
This is the panel content.
|
||
</x-mail::panel>
|
||
|
||
#### Table Component
|
||
|
||
The table component allows you to transform a Markdown table into an HTML
|
||
table. The component accepts the Markdown table as its content. Table column
|
||
alignment is supported using the default Markdown table alignment syntax:
|
||
|
||
|
||
|
||
1<x-mail::table>
|
||
|
||
2| Laravel | Table | Example |
|
||
|
||
3| ------------- | :-----------: | ------------: |
|
||
|
||
4| Col 2 is | Centered | $10 |
|
||
|
||
5| Col 3 is | Right-Aligned | $20 |
|
||
|
||
6</x-mail::table>
|
||
|
||
|
||
<x-mail::table>
|
||
| Laravel | Table | Example |
|
||
| ------------- | :-----------: | ------------: |
|
||
| Col 2 is | Centered | $10 |
|
||
| Col 3 is | Right-Aligned | $20 |
|
||
</x-mail::table>
|
||
|
||
### Customizing the Components
|
||
|
||
You may export all of the Markdown notification components to your own
|
||
application for customization. To export the components, use the
|
||
`vendor:publish` Artisan command to publish the `laravel-mail` asset tag:
|
||
|
||
|
||
|
||
1php artisan vendor:publish --tag=laravel-mail
|
||
|
||
|
||
php artisan vendor:publish --tag=laravel-mail
|
||
|
||
This command will publish the Markdown mail components to the
|
||
`resources/views/vendor/mail` directory. The `mail` directory will contain an
|
||
`html` and a `text` directory, each containing their respective
|
||
representations of every available component. You are free to customize these
|
||
components however you like.
|
||
|
||
#### Customizing the CSS
|
||
|
||
After exporting the components, the `resources/views/vendor/mail/html/themes`
|
||
directory will contain a `default.css` file. You may customize the CSS in this
|
||
file and your styles will automatically be in-lined within the HTML
|
||
representations of your Markdown notifications.
|
||
|
||
If you would like to build an entirely new theme for Laravel's Markdown
|
||
components, you may place a CSS file within the `html/themes` directory. After
|
||
naming and saving your CSS file, update the `theme` option of the `mail`
|
||
configuration file to match the name of your new theme.
|
||
|
||
To customize the theme for an individual notification, you may call the
|
||
`theme` method while building the notification's mail message. The `theme`
|
||
method accepts the name of the theme that should be used when sending the
|
||
notification:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the mail representation of the notification.
|
||
|
||
3 */
|
||
|
||
4public function toMail(object $notifiable): MailMessage
|
||
|
||
5{
|
||
|
||
6 return (new MailMessage)
|
||
|
||
7 ->theme('invoice')
|
||
|
||
8 ->subject('Invoice Paid')
|
||
|
||
9 ->markdown('mail.invoice.paid', ['url' => $url]);
|
||
|
||
10}
|
||
|
||
|
||
/**
|
||
* Get the mail representation of the notification.
|
||
*/
|
||
public function toMail(object $notifiable): MailMessage
|
||
{
|
||
return (new MailMessage)
|
||
->theme('invoice')
|
||
->subject('Invoice Paid')
|
||
->markdown('mail.invoice.paid', ['url' => $url]);
|
||
}
|
||
|
||
## Database Notifications
|
||
|
||
### Prerequisites
|
||
|
||
The `database` notification channel stores the notification information in a
|
||
database table. This table will contain information such as the notification
|
||
type as well as a JSON data structure that describes the notification.
|
||
|
||
You can query the table to display the notifications in your application's
|
||
user interface. But, before you can do that, you will need to create a
|
||
database table to hold your notifications. You may use the
|
||
`make:notifications-table` command to generate a
|
||
[migration](/docs/12.x/migrations) with the proper table schema:
|
||
|
||
|
||
|
||
1php artisan make:notifications-table
|
||
|
||
2
|
||
|
||
3php artisan migrate
|
||
|
||
|
||
php artisan make:notifications-table
|
||
|
||
php artisan migrate
|
||
|
||
If your notifiable models are using [UUID or ULID primary
|
||
keys](/docs/12.x/eloquent#uuid-and-ulid-keys), you should replace the `morphs`
|
||
method with [uuidMorphs](/docs/12.x/migrations#column-method-uuidMorphs) or
|
||
[ulidMorphs](/docs/12.x/migrations#column-method-ulidMorphs) in the
|
||
notification table migration.
|
||
|
||
### Formatting Database Notifications
|
||
|
||
If a notification supports being stored in a database table, you should define
|
||
a `toDatabase` or `toArray` method on the notification class. This method will
|
||
receive a `$notifiable` entity and should return a plain PHP array. The
|
||
returned array will be encoded as JSON and stored in the `data` column of your
|
||
`notifications` table. Let's take a look at an example `toArray` method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the array representation of the notification.
|
||
|
||
3 *
|
||
|
||
4 * @return array<string, mixed>
|
||
|
||
5 */
|
||
|
||
6public function toArray(object $notifiable): array
|
||
|
||
7{
|
||
|
||
8 return [
|
||
|
||
9 'invoice_id' => $this->invoice->id,
|
||
|
||
10 'amount' => $this->invoice->amount,
|
||
|
||
11 ];
|
||
|
||
12}
|
||
|
||
|
||
/**
|
||
* Get the array representation of the notification.
|
||
*
|
||
* @return array<string, mixed>
|
||
*/
|
||
public function toArray(object $notifiable): array
|
||
{
|
||
return [
|
||
'invoice_id' => $this->invoice->id,
|
||
'amount' => $this->invoice->amount,
|
||
];
|
||
}
|
||
|
||
When a notification is stored in your application's database, the `type`
|
||
column will be set to the notification's class name by default, and the
|
||
`read_at` column will be `null`. However, you can customize this behavior by
|
||
defining the `databaseType` and `initialDatabaseReadAtValue` methods in your
|
||
notification class:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Carbon;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the notification's database type.
|
||
|
||
5 */
|
||
|
||
6public function databaseType(object $notifiable): string
|
||
|
||
7{
|
||
|
||
8 return 'invoice-paid';
|
||
|
||
9}
|
||
|
||
10
|
||
|
||
11/**
|
||
|
||
12 * Get the initial value for the "read_at" column.
|
||
|
||
13 */
|
||
|
||
14public function initialDatabaseReadAtValue(): ?Carbon
|
||
|
||
15{
|
||
|
||
16 return null;
|
||
|
||
17}
|
||
|
||
|
||
use Illuminate\Support\Carbon;
|
||
|
||
/**
|
||
* Get the notification's database type.
|
||
*/
|
||
public function databaseType(object $notifiable): string
|
||
{
|
||
return 'invoice-paid';
|
||
}
|
||
|
||
/**
|
||
* Get the initial value for the "read_at" column.
|
||
*/
|
||
public function initialDatabaseReadAtValue(): ?Carbon
|
||
{
|
||
return null;
|
||
}
|
||
|
||
#### `toDatabase` vs. `toArray`
|
||
|
||
The `toArray` method is also used by the `broadcast` channel to determine
|
||
which data to broadcast to your JavaScript powered frontend. If you would like
|
||
to have two different array representations for the `database` and `broadcast`
|
||
channels, you should define a `toDatabase` method instead of a `toArray`
|
||
method.
|
||
|
||
### Accessing the Notifications
|
||
|
||
Once notifications are stored in the database, you need a convenient way to
|
||
access them from your notifiable entities. The
|
||
`Illuminate\Notifications\Notifiable` trait, which is included on Laravel's
|
||
default `App\Models\User` model, includes a `notifications` [Eloquent
|
||
relationship](/docs/12.x/eloquent-relationships) that returns the
|
||
notifications for the entity. To fetch notifications, you may access this
|
||
method like any other Eloquent relationship. By default, notifications will be
|
||
sorted by the `created_at` timestamp with the most recent notifications at the
|
||
beginning of the collection:
|
||
|
||
|
||
|
||
1$user = App\Models\User::find(1);
|
||
|
||
2
|
||
|
||
3foreach ($user->notifications as $notification) {
|
||
|
||
4 echo $notification->type;
|
||
|
||
5}
|
||
|
||
|
||
$user = App\Models\User::find(1);
|
||
|
||
foreach ($user->notifications as $notification) {
|
||
echo $notification->type;
|
||
}
|
||
|
||
If you want to retrieve only the "unread" notifications, you may use the
|
||
`unreadNotifications` relationship. Again, these notifications will be sorted
|
||
by the `created_at` timestamp with the most recent notifications at the
|
||
beginning of the collection:
|
||
|
||
|
||
|
||
1$user = App\Models\User::find(1);
|
||
|
||
2
|
||
|
||
3foreach ($user->unreadNotifications as $notification) {
|
||
|
||
4 echo $notification->type;
|
||
|
||
5}
|
||
|
||
|
||
$user = App\Models\User::find(1);
|
||
|
||
foreach ($user->unreadNotifications as $notification) {
|
||
echo $notification->type;
|
||
}
|
||
|
||
If you want to retrieve only the "read" notifications, you may use the
|
||
`readNotifications` relationship:
|
||
|
||
|
||
|
||
1$user = App\Models\User::find(1);
|
||
|
||
2
|
||
|
||
3foreach ($user->readNotifications as $notification) {
|
||
|
||
4 echo $notification->type;
|
||
|
||
5}
|
||
|
||
|
||
$user = App\Models\User::find(1);
|
||
|
||
foreach ($user->readNotifications as $notification) {
|
||
echo $notification->type;
|
||
}
|
||
|
||
To access your notifications from your JavaScript client, you should define a
|
||
notification controller for your application which returns the notifications
|
||
for a notifiable entity, such as the current user. You may then make an HTTP
|
||
request to that controller's URL from your JavaScript client.
|
||
|
||
### Marking Notifications as Read
|
||
|
||
Typically, you will want to mark a notification as "read" when a user views
|
||
it. The `Illuminate\Notifications\Notifiable` trait provides a `markAsRead`
|
||
method, which updates the `read_at` column on the notification's database
|
||
record:
|
||
|
||
|
||
|
||
1$user = App\Models\User::find(1);
|
||
|
||
2
|
||
|
||
3foreach ($user->unreadNotifications as $notification) {
|
||
|
||
4 $notification->markAsRead();
|
||
|
||
5}
|
||
|
||
|
||
$user = App\Models\User::find(1);
|
||
|
||
foreach ($user->unreadNotifications as $notification) {
|
||
$notification->markAsRead();
|
||
}
|
||
|
||
However, instead of looping through each notification, you may use the
|
||
`markAsRead` method directly on a collection of notifications:
|
||
|
||
|
||
|
||
1$user->unreadNotifications->markAsRead();
|
||
|
||
|
||
$user->unreadNotifications->markAsRead();
|
||
|
||
You may also use a mass-update query to mark all of the notifications as read
|
||
without retrieving them from the database:
|
||
|
||
|
||
|
||
1$user = App\Models\User::find(1);
|
||
|
||
2
|
||
|
||
3$user->unreadNotifications()->update(['read_at' => now()]);
|
||
|
||
|
||
$user = App\Models\User::find(1);
|
||
|
||
$user->unreadNotifications()->update(['read_at' => now()]);
|
||
|
||
You may `delete` the notifications to remove them from the table entirely:
|
||
|
||
|
||
|
||
1$user->notifications()->delete();
|
||
|
||
|
||
$user->notifications()->delete();
|
||
|
||
## Broadcast Notifications
|
||
|
||
### Prerequisites
|
||
|
||
Before broadcasting notifications, you should configure and be familiar with
|
||
Laravel's [event broadcasting](/docs/12.x/broadcasting) services. Event
|
||
broadcasting provides a way to react to server-side Laravel events from your
|
||
JavaScript powered frontend.
|
||
|
||
### Formatting Broadcast Notifications
|
||
|
||
The `broadcast` channel broadcasts notifications using Laravel's [event
|
||
broadcasting](/docs/12.x/broadcasting) services, allowing your JavaScript
|
||
powered frontend to catch notifications in realtime. If a notification
|
||
supports broadcasting, you can define a `toBroadcast` method on the
|
||
notification class. This method will receive a `$notifiable` entity and should
|
||
return a `BroadcastMessage` instance. If the `toBroadcast` method does not
|
||
exist, the `toArray` method will be used to gather the data that should be
|
||
broadcast. The returned data will be encoded as JSON and broadcast to your
|
||
JavaScript powered frontend. Let's take a look at an example `toBroadcast`
|
||
method:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Messages\BroadcastMessage;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the broadcastable representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toBroadcast(object $notifiable): BroadcastMessage
|
||
|
||
7{
|
||
|
||
8 return new BroadcastMessage([
|
||
|
||
9 'invoice_id' => $this->invoice->id,
|
||
|
||
10 'amount' => $this->invoice->amount,
|
||
|
||
11 ]);
|
||
|
||
12}
|
||
|
||
|
||
use Illuminate\Notifications\Messages\BroadcastMessage;
|
||
|
||
/**
|
||
* Get the broadcastable representation of the notification.
|
||
*/
|
||
public function toBroadcast(object $notifiable): BroadcastMessage
|
||
{
|
||
return new BroadcastMessage([
|
||
'invoice_id' => $this->invoice->id,
|
||
'amount' => $this->invoice->amount,
|
||
]);
|
||
}
|
||
|
||
#### Broadcast Queue Configuration
|
||
|
||
All broadcast notifications are queued for broadcasting. If you would like to
|
||
configure the queue connection or queue name that is used to queue the
|
||
broadcast operation, you may use the `onConnection` and `onQueue` methods of
|
||
the `BroadcastMessage`:
|
||
|
||
|
||
|
||
1return (new BroadcastMessage($data))
|
||
|
||
2 ->onConnection('sqs')
|
||
|
||
3 ->onQueue('broadcasts');
|
||
|
||
|
||
return (new BroadcastMessage($data))
|
||
->onConnection('sqs')
|
||
->onQueue('broadcasts');
|
||
|
||
#### Customizing the Notification Type
|
||
|
||
In addition to the data you specify, all broadcast notifications also have a
|
||
`type` field containing the full class name of the notification. If you would
|
||
like to customize the notification `type`, you may define a `broadcastType`
|
||
method on the notification class:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Get the type of the notification being broadcast.
|
||
|
||
3 */
|
||
|
||
4public function broadcastType(): string
|
||
|
||
5{
|
||
|
||
6 return 'broadcast.message';
|
||
|
||
7}
|
||
|
||
|
||
/**
|
||
* Get the type of the notification being broadcast.
|
||
*/
|
||
public function broadcastType(): string
|
||
{
|
||
return 'broadcast.message';
|
||
}
|
||
|
||
### Listening for Notifications
|
||
|
||
Notifications will broadcast on a private channel formatted using a
|
||
`{notifiable}.{id}` convention. So, if you are sending a notification to an
|
||
`App\Models\User` instance with an ID of `1`, the notification will be
|
||
broadcast on the `App.Models.User.1` private channel. When using [Laravel
|
||
Echo](/docs/12.x/broadcasting#client-side-installation), you may easily listen
|
||
for notifications on a channel using the `notification` method:
|
||
|
||
|
||
|
||
1Echo.private('App.Models.User.' + userId)
|
||
|
||
2 .notification((notification) => {
|
||
|
||
3 console.log(notification.type);
|
||
|
||
4 });
|
||
|
||
|
||
Echo.private('App.Models.User.' + userId)
|
||
.notification((notification) => {
|
||
console.log(notification.type);
|
||
});
|
||
|
||
#### Using React or Vue
|
||
|
||
Laravel Echo includes React and Vue hooks that make it painless to listen for
|
||
notifications. To get started, invoke the `useEchoNotification` hook, which is
|
||
used to listen for notifications. The `useEchoNotification` hook will
|
||
automatically leave channels when the consuming component is unmounted:
|
||
|
||
React Vue
|
||
|
||
|
||
|
||
1import { useEchoNotification } from "@laravel/echo-react";
|
||
|
||
2
|
||
|
||
3useEchoNotification(
|
||
|
||
4 `App.Models.User.${userId}`,
|
||
|
||
5 (notification) => {
|
||
|
||
6 console.log(notification.type);
|
||
|
||
7 },
|
||
|
||
8);
|
||
|
||
|
||
import { useEchoNotification } from "@laravel/echo-react";
|
||
|
||
useEchoNotification(
|
||
`App.Models.User.${userId}`,
|
||
(notification) => {
|
||
console.log(notification.type);
|
||
},
|
||
);
|
||
|
||
|
||
1<script setup lang="ts">
|
||
|
||
2import { useEchoNotification } from "@laravel/echo-vue";
|
||
|
||
3
|
||
|
||
4useEchoNotification(
|
||
|
||
5 `App.Models.User.${userId}`,
|
||
|
||
6 (notification) => {
|
||
|
||
7 console.log(notification.type);
|
||
|
||
8 },
|
||
|
||
9);
|
||
|
||
10</script>
|
||
|
||
|
||
<script setup lang="ts">
|
||
import { useEchoNotification } from "@laravel/echo-vue";
|
||
|
||
useEchoNotification(
|
||
`App.Models.User.${userId}`,
|
||
(notification) => {
|
||
console.log(notification.type);
|
||
},
|
||
);
|
||
</script>
|
||
|
||
By default, the hook listens to all notifications. To specify the notification
|
||
types you would like to listen to, you can provide either a string or array of
|
||
types to `useEchoNotification`:
|
||
|
||
React Vue
|
||
|
||
|
||
|
||
1import { useEchoNotification } from "@laravel/echo-react";
|
||
|
||
2
|
||
|
||
3useEchoNotification(
|
||
|
||
4 `App.Models.User.${userId}`,
|
||
|
||
5 (notification) => {
|
||
|
||
6 console.log(notification.type);
|
||
|
||
7 },
|
||
|
||
8 'App.Notifications.InvoicePaid',
|
||
|
||
9);
|
||
|
||
|
||
import { useEchoNotification } from "@laravel/echo-react";
|
||
|
||
useEchoNotification(
|
||
`App.Models.User.${userId}`,
|
||
(notification) => {
|
||
console.log(notification.type);
|
||
},
|
||
'App.Notifications.InvoicePaid',
|
||
);
|
||
|
||
|
||
1<script setup lang="ts">
|
||
|
||
2import { useEchoNotification } from "@laravel/echo-vue";
|
||
|
||
3
|
||
|
||
4useEchoNotification(
|
||
|
||
5 `App.Models.User.${userId}`,
|
||
|
||
6 (notification) => {
|
||
|
||
7 console.log(notification.type);
|
||
|
||
8 },
|
||
|
||
9 'App.Notifications.InvoicePaid',
|
||
|
||
10);
|
||
|
||
11</script>
|
||
|
||
|
||
<script setup lang="ts">
|
||
import { useEchoNotification } from "@laravel/echo-vue";
|
||
|
||
useEchoNotification(
|
||
`App.Models.User.${userId}`,
|
||
(notification) => {
|
||
console.log(notification.type);
|
||
},
|
||
'App.Notifications.InvoicePaid',
|
||
);
|
||
</script>
|
||
|
||
You may also specify the shape of the notification payload data, providing
|
||
greater type safety and editing convenience:
|
||
|
||
|
||
|
||
1type InvoicePaidNotification = {
|
||
|
||
2 invoice_id: number;
|
||
|
||
3 created_at: string;
|
||
|
||
4};
|
||
|
||
5
|
||
|
||
6useEchoNotification<InvoicePaidNotification>(
|
||
|
||
7 `App.Models.User.${userId}`,
|
||
|
||
8 (notification) => {
|
||
|
||
9 console.log(notification.invoice_id);
|
||
|
||
10 console.log(notification.created_at);
|
||
|
||
11 console.log(notification.type);
|
||
|
||
12 },
|
||
|
||
13 'App.Notifications.InvoicePaid',
|
||
|
||
14);
|
||
|
||
|
||
type InvoicePaidNotification = {
|
||
invoice_id: number;
|
||
created_at: string;
|
||
};
|
||
|
||
useEchoNotification<InvoicePaidNotification>(
|
||
`App.Models.User.${userId}`,
|
||
(notification) => {
|
||
console.log(notification.invoice_id);
|
||
console.log(notification.created_at);
|
||
console.log(notification.type);
|
||
},
|
||
'App.Notifications.InvoicePaid',
|
||
);
|
||
|
||
#### Customizing the Notification Channel
|
||
|
||
If you would like to customize which channel that an entity's broadcast
|
||
notifications are broadcast on, you may define a
|
||
`receivesBroadcastNotificationsOn` method on the notifiable entity:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Broadcasting\PrivateChannel;
|
||
|
||
6use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
|
||
7use Illuminate\Notifications\Notifiable;
|
||
|
||
8
|
||
|
||
9class User extends Authenticatable
|
||
|
||
10{
|
||
|
||
11 use Notifiable;
|
||
|
||
12
|
||
|
||
13 /**
|
||
|
||
14 * The channels the user receives notification broadcasts on.
|
||
|
||
15 */
|
||
|
||
16 public function receivesBroadcastNotificationsOn(): string
|
||
|
||
17 {
|
||
|
||
18 return 'users.'.$this->id;
|
||
|
||
19 }
|
||
|
||
20}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Broadcasting\PrivateChannel;
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use Notifiable;
|
||
|
||
/**
|
||
* The channels the user receives notification broadcasts on.
|
||
*/
|
||
public function receivesBroadcastNotificationsOn(): string
|
||
{
|
||
return 'users.'.$this->id;
|
||
}
|
||
}
|
||
|
||
## SMS Notifications
|
||
|
||
### Prerequisites
|
||
|
||
Sending SMS notifications in Laravel is powered by
|
||
[Vonage](https://www.vonage.com/) (formerly known as Nexmo). Before you can
|
||
send notifications via Vonage, you need to install the `laravel/vonage-
|
||
notification-channel` and `guzzlehttp/guzzle` packages:
|
||
|
||
|
||
|
||
1composer require laravel/vonage-notification-channel guzzlehttp/guzzle
|
||
|
||
|
||
composer require laravel/vonage-notification-channel guzzlehttp/guzzle
|
||
|
||
The package includes a [configuration file](https://github.com/laravel/vonage-
|
||
notification-channel/blob/3.x/config/vonage.php). However, you are not
|
||
required to export this configuration file to your own application. You can
|
||
simply use the `VONAGE_KEY` and `VONAGE_SECRET` environment variables to
|
||
define your Vonage public and secret keys.
|
||
|
||
After defining your keys, you should set a `VONAGE_SMS_FROM` environment
|
||
variable that defines the phone number that your SMS messages should be sent
|
||
from by default. You may generate this phone number within the Vonage control
|
||
panel:
|
||
|
||
|
||
|
||
1VONAGE_SMS_FROM=15556666666
|
||
|
||
|
||
VONAGE_SMS_FROM=15556666666
|
||
|
||
### Formatting SMS Notifications
|
||
|
||
If a notification supports being sent as an SMS, you should define a
|
||
`toVonage` method on the notification class. This method will receive a
|
||
`$notifiable` entity and should return an
|
||
`Illuminate\Notifications\Messages\VonageMessage` instance:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the Vonage / SMS representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toVonage(object $notifiable): VonageMessage
|
||
|
||
7{
|
||
|
||
8 return (new VonageMessage)
|
||
|
||
9 ->content('Your SMS message content');
|
||
|
||
10}
|
||
|
||
|
||
use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
/**
|
||
* Get the Vonage / SMS representation of the notification.
|
||
*/
|
||
public function toVonage(object $notifiable): VonageMessage
|
||
{
|
||
return (new VonageMessage)
|
||
->content('Your SMS message content');
|
||
}
|
||
|
||
#### Unicode Content
|
||
|
||
If your SMS message will contain unicode characters, you should call the
|
||
`unicode` method when constructing the `VonageMessage` instance:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the Vonage / SMS representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toVonage(object $notifiable): VonageMessage
|
||
|
||
7{
|
||
|
||
8 return (new VonageMessage)
|
||
|
||
9 ->content('Your unicode message')
|
||
|
||
10 ->unicode();
|
||
|
||
11}
|
||
|
||
|
||
use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
/**
|
||
* Get the Vonage / SMS representation of the notification.
|
||
*/
|
||
public function toVonage(object $notifiable): VonageMessage
|
||
{
|
||
return (new VonageMessage)
|
||
->content('Your unicode message')
|
||
->unicode();
|
||
}
|
||
|
||
### Customizing the "From" Number
|
||
|
||
If you would like to send some notifications from a phone number that is
|
||
different from the phone number specified by your `VONAGE_SMS_FROM`
|
||
environment variable, you may call the `from` method on a `VonageMessage`
|
||
instance:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the Vonage / SMS representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toVonage(object $notifiable): VonageMessage
|
||
|
||
7{
|
||
|
||
8 return (new VonageMessage)
|
||
|
||
9 ->content('Your SMS message content')
|
||
|
||
10 ->from('15554443333');
|
||
|
||
11}
|
||
|
||
|
||
use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
/**
|
||
* Get the Vonage / SMS representation of the notification.
|
||
*/
|
||
public function toVonage(object $notifiable): VonageMessage
|
||
{
|
||
return (new VonageMessage)
|
||
->content('Your SMS message content')
|
||
->from('15554443333');
|
||
}
|
||
|
||
### Adding a Client Reference
|
||
|
||
If you would like to keep track of costs per user, team, or client, you may
|
||
add a "client reference" to the notification. Vonage will allow you to
|
||
generate reports using this client reference so that you can better understand
|
||
a particular customer's SMS usage. The client reference can be any string up
|
||
to 40 characters:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Get the Vonage / SMS representation of the notification.
|
||
|
||
5 */
|
||
|
||
6public function toVonage(object $notifiable): VonageMessage
|
||
|
||
7{
|
||
|
||
8 return (new VonageMessage)
|
||
|
||
9 ->clientReference((string) $notifiable->id)
|
||
|
||
10 ->content('Your SMS message content');
|
||
|
||
11}
|
||
|
||
|
||
use Illuminate\Notifications\Messages\VonageMessage;
|
||
|
||
/**
|
||
* Get the Vonage / SMS representation of the notification.
|
||
*/
|
||
public function toVonage(object $notifiable): VonageMessage
|
||
{
|
||
return (new VonageMessage)
|
||
->clientReference((string) $notifiable->id)
|
||
->content('Your SMS message content');
|
||
}
|
||
|
||
### Routing SMS Notifications
|
||
|
||
To route Vonage notifications to the proper phone number, define a
|
||
`routeNotificationForVonage` method on your notifiable entity:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
|
||
6use Illuminate\Notifications\Notifiable;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8
|
||
|
||
9class User extends Authenticatable
|
||
|
||
10{
|
||
|
||
11 use Notifiable;
|
||
|
||
12
|
||
|
||
13 /**
|
||
|
||
14 * Route notifications for the Vonage channel.
|
||
|
||
15 */
|
||
|
||
16 public function routeNotificationForVonage(Notification $notification): string
|
||
|
||
17 {
|
||
|
||
18 return $this->phone_number;
|
||
|
||
19 }
|
||
|
||
20}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use Notifiable;
|
||
|
||
/**
|
||
* Route notifications for the Vonage channel.
|
||
*/
|
||
public function routeNotificationForVonage(Notification $notification): string
|
||
{
|
||
return $this->phone_number;
|
||
}
|
||
}
|
||
|
||
## Slack Notifications
|
||
|
||
### Prerequisites
|
||
|
||
Before sending Slack notifications, you should install the Slack notification
|
||
channel via Composer:
|
||
|
||
|
||
|
||
1composer require laravel/slack-notification-channel
|
||
|
||
|
||
composer require laravel/slack-notification-channel
|
||
|
||
Additionally, you must create a [Slack
|
||
App](https://api.slack.com/apps?new_app=1) for your Slack workspace.
|
||
|
||
If you only need to send notifications to the same Slack workspace that the
|
||
App is created in, you should ensure that your App has the `chat:write`,
|
||
`chat:write.public`, and `chat:write.customize` scopes. These scopes can be
|
||
added from the "OAuth & Permissions" App management tab within Slack.
|
||
|
||
Next, copy the App's "Bot User OAuth Token" and place it within a `slack`
|
||
configuration array in your application's `services.php` configuration file.
|
||
This token can be found on the "OAuth & Permissions" tab within Slack:
|
||
|
||
|
||
|
||
1'slack' => [
|
||
|
||
2 'notifications' => [
|
||
|
||
3 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||
|
||
4 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||
|
||
5 ],
|
||
|
||
6],
|
||
|
||
|
||
'slack' => [
|
||
'notifications' => [
|
||
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||
],
|
||
],
|
||
|
||
#### App Distribution
|
||
|
||
If your application will be sending notifications to external Slack workspaces
|
||
that are owned by your application's users, you will need to "distribute" your
|
||
App via Slack. App distribution can be managed from your App's "Manage
|
||
Distribution" tab within Slack. Once your App has been distributed, you may
|
||
use [Socialite](/docs/12.x/socialite) to [obtain Slack Bot
|
||
tokens](/docs/12.x/socialite#slack-bot-scopes) on behalf of your application's
|
||
users.
|
||
|
||
### Formatting Slack Notifications
|
||
|
||
If a notification supports being sent as a Slack message, you should define a
|
||
`toSlack` method on the notification class. This method will receive a
|
||
`$notifiable` entity and should return an
|
||
`Illuminate\Notifications\Slack\SlackMessage` instance. You can construct rich
|
||
notifications using [Slack's Block Kit API](https://api.slack.com/block-kit).
|
||
The following example may be previewed in [Slack's Block Kit
|
||
builder](https://app.slack.com/block-kit-
|
||
builder/T01KWS6K23Z#%7B%22blocks%22:%5B%7B%22type%22:%22header%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Invoice%20Paid%22%7D%7D,%7B%22type%22:%22context%22,%22elements%22:%5B%7B%22type%22:%22plain_text%22,%22text%22:%22Customer%20%231234%22%7D%5D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22An%20invoice%20has%20been%20paid.%22%7D,%22fields%22:%5B%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Invoice%20No:*%5Cn1000%22%7D,%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Invoice%20Recipient:*%5Cntaylor@laravel.com%22%7D%5D%7D,%7B%22type%22:%22divider%22%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Congratulations!%22%7D%7D%5D%7D):
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
|
||
|
||
2use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
|
||
|
||
3use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
4
|
||
|
||
5/**
|
||
|
||
6 * Get the Slack representation of the notification.
|
||
|
||
7 */
|
||
|
||
8public function toSlack(object $notifiable): SlackMessage
|
||
|
||
9{
|
||
|
||
10 return (new SlackMessage)
|
||
|
||
11 ->text('One of your invoices has been paid!')
|
||
|
||
12 ->headerBlock('Invoice Paid')
|
||
|
||
13 ->contextBlock(function (ContextBlock $block) {
|
||
|
||
14 $block->text('Customer #1234');
|
||
|
||
15 })
|
||
|
||
16 ->sectionBlock(function (SectionBlock $block) {
|
||
|
||
17 $block->text('An invoice has been paid.');
|
||
|
||
18 $block->field("*Invoice No:*\n1000")->markdown();
|
||
|
||
19 $block->field("*Invoice Recipient:*\n[[email protected]](/cdn-cgi/l/email-protection)")->markdown();
|
||
|
||
20 })
|
||
|
||
21 ->dividerBlock()
|
||
|
||
22 ->sectionBlock(function (SectionBlock $block) {
|
||
|
||
23 $block->text('Congratulations!');
|
||
|
||
24 });
|
||
|
||
25}
|
||
|
||
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
|
||
use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
/**
|
||
* Get the Slack representation of the notification.
|
||
*/
|
||
public function toSlack(object $notifiable): SlackMessage
|
||
{
|
||
return (new SlackMessage)
|
||
->text('One of your invoices has been paid!')
|
||
->headerBlock('Invoice Paid')
|
||
->contextBlock(function (ContextBlock $block) {
|
||
$block->text('Customer #1234');
|
||
})
|
||
->sectionBlock(function (SectionBlock $block) {
|
||
$block->text('An invoice has been paid.');
|
||
$block->field("*Invoice No:*\n1000")->markdown();
|
||
$block->field("*Invoice Recipient:*\[[email protected]](/cdn-cgi/l/email-protection)")->markdown();
|
||
})
|
||
->dividerBlock()
|
||
->sectionBlock(function (SectionBlock $block) {
|
||
$block->text('Congratulations!');
|
||
});
|
||
}
|
||
|
||
#### Using Slack's Block Kit Builder Template
|
||
|
||
Instead of using the fluent message builder methods to construct your Block
|
||
Kit message, you may provide the raw JSON payload generated by Slack's Block
|
||
Kit Builder to the `usingBlockKitTemplate` method:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
2use Illuminate\Support\Str;
|
||
|
||
3
|
||
|
||
4/**
|
||
|
||
5 * Get the Slack representation of the notification.
|
||
|
||
6 */
|
||
|
||
7public function toSlack(object $notifiable): SlackMessage
|
||
|
||
8{
|
||
|
||
9 $template = <<<JSON
|
||
|
||
10 {
|
||
|
||
11 "blocks": [
|
||
|
||
12 {
|
||
|
||
13 "type": "header",
|
||
|
||
14 "text": {
|
||
|
||
15 "type": "plain_text",
|
||
|
||
16 "text": "Team Announcement"
|
||
|
||
17 }
|
||
|
||
18 },
|
||
|
||
19 {
|
||
|
||
20 "type": "section",
|
||
|
||
21 "text": {
|
||
|
||
22 "type": "plain_text",
|
||
|
||
23 "text": "We are hiring!"
|
||
|
||
24 }
|
||
|
||
25 }
|
||
|
||
26 ]
|
||
|
||
27 }
|
||
|
||
28 JSON;
|
||
|
||
29
|
||
|
||
30 return (new SlackMessage)
|
||
|
||
31 ->usingBlockKitTemplate($template);
|
||
|
||
32}
|
||
|
||
|
||
use Illuminate\Notifications\Slack\SlackMessage;
|
||
use Illuminate\Support\Str;
|
||
|
||
/**
|
||
* Get the Slack representation of the notification.
|
||
*/
|
||
public function toSlack(object $notifiable): SlackMessage
|
||
{
|
||
$template = <<<JSON
|
||
{
|
||
"blocks": [
|
||
{
|
||
"type": "header",
|
||
"text": {
|
||
"type": "plain_text",
|
||
"text": "Team Announcement"
|
||
}
|
||
},
|
||
{
|
||
"type": "section",
|
||
"text": {
|
||
"type": "plain_text",
|
||
"text": "We are hiring!"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
JSON;
|
||
|
||
return (new SlackMessage)
|
||
->usingBlockKitTemplate($template);
|
||
}
|
||
|
||
### Slack Interactivity
|
||
|
||
Slack's Block Kit notification system provides powerful features to [handle
|
||
user interaction](https://api.slack.com/interactivity/handling). To utilize
|
||
these features, your Slack App should have "Interactivity" enabled and a
|
||
"Request URL" configured that points to a URL served by your application.
|
||
These settings can be managed from the "Interactivity & Shortcuts" App
|
||
management tab within Slack.
|
||
|
||
In the following example, which utilizes the `actionsBlock` method, Slack will
|
||
send a `POST` request to your "Request URL" with a payload containing the
|
||
Slack user who clicked the button, the ID of the clicked button, and more.
|
||
Your application can then determine the action to take based on the payload.
|
||
You should also [verify the
|
||
request](https://api.slack.com/authentication/verifying-requests-from-slack)
|
||
was made by Slack:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
|
||
|
||
2use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
|
||
|
||
3use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
|
||
|
||
4use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
5
|
||
|
||
6/**
|
||
|
||
7 * Get the Slack representation of the notification.
|
||
|
||
8 */
|
||
|
||
9public function toSlack(object $notifiable): SlackMessage
|
||
|
||
10{
|
||
|
||
11 return (new SlackMessage)
|
||
|
||
12 ->text('One of your invoices has been paid!')
|
||
|
||
13 ->headerBlock('Invoice Paid')
|
||
|
||
14 ->contextBlock(function (ContextBlock $block) {
|
||
|
||
15 $block->text('Customer #1234');
|
||
|
||
16 })
|
||
|
||
17 ->sectionBlock(function (SectionBlock $block) {
|
||
|
||
18 $block->text('An invoice has been paid.');
|
||
|
||
19 })
|
||
|
||
20 ->actionsBlock(function (ActionsBlock $block) {
|
||
|
||
21 // ID defaults to "button_acknowledge_invoice"...
|
||
|
||
22 $block->button('Acknowledge Invoice')->primary();
|
||
|
||
23
|
||
|
||
24 // Manually configure the ID...
|
||
|
||
25 $block->button('Deny')->danger()->id('deny_invoice');
|
||
|
||
26 });
|
||
|
||
27}
|
||
|
||
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
|
||
use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
/**
|
||
* Get the Slack representation of the notification.
|
||
*/
|
||
public function toSlack(object $notifiable): SlackMessage
|
||
{
|
||
return (new SlackMessage)
|
||
->text('One of your invoices has been paid!')
|
||
->headerBlock('Invoice Paid')
|
||
->contextBlock(function (ContextBlock $block) {
|
||
$block->text('Customer #1234');
|
||
})
|
||
->sectionBlock(function (SectionBlock $block) {
|
||
$block->text('An invoice has been paid.');
|
||
})
|
||
->actionsBlock(function (ActionsBlock $block) {
|
||
// ID defaults to "button_acknowledge_invoice"...
|
||
$block->button('Acknowledge Invoice')->primary();
|
||
|
||
// Manually configure the ID...
|
||
$block->button('Deny')->danger()->id('deny_invoice');
|
||
});
|
||
}
|
||
|
||
#### Confirmation Modals
|
||
|
||
If you would like users to be required to confirm an action before it is
|
||
performed, you may invoke the `confirm` method when defining your button. The
|
||
`confirm` method accepts a message and a closure which receives a
|
||
`ConfirmObject` instance:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
|
||
|
||
2use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
|
||
|
||
3use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
|
||
|
||
4use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject;
|
||
|
||
5use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
6
|
||
|
||
7/**
|
||
|
||
8 * Get the Slack representation of the notification.
|
||
|
||
9 */
|
||
|
||
10public function toSlack(object $notifiable): SlackMessage
|
||
|
||
11{
|
||
|
||
12 return (new SlackMessage)
|
||
|
||
13 ->text('One of your invoices has been paid!')
|
||
|
||
14 ->headerBlock('Invoice Paid')
|
||
|
||
15 ->contextBlock(function (ContextBlock $block) {
|
||
|
||
16 $block->text('Customer #1234');
|
||
|
||
17 })
|
||
|
||
18 ->sectionBlock(function (SectionBlock $block) {
|
||
|
||
19 $block->text('An invoice has been paid.');
|
||
|
||
20 })
|
||
|
||
21 ->actionsBlock(function (ActionsBlock $block) {
|
||
|
||
22 $block->button('Acknowledge Invoice')
|
||
|
||
23 ->primary()
|
||
|
||
24 ->confirm(
|
||
|
||
25 'Acknowledge the payment and send a thank you email?',
|
||
|
||
26 function (ConfirmObject $dialog) {
|
||
|
||
27 $dialog->confirm('Yes');
|
||
|
||
28 $dialog->deny('No');
|
||
|
||
29 }
|
||
|
||
30 );
|
||
|
||
31 });
|
||
|
||
32}
|
||
|
||
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
|
||
use Illuminate\Notifications\Slack\BlockKit\Blocks\SectionBlock;
|
||
use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject;
|
||
use Illuminate\Notifications\Slack\SlackMessage;
|
||
|
||
/**
|
||
* Get the Slack representation of the notification.
|
||
*/
|
||
public function toSlack(object $notifiable): SlackMessage
|
||
{
|
||
return (new SlackMessage)
|
||
->text('One of your invoices has been paid!')
|
||
->headerBlock('Invoice Paid')
|
||
->contextBlock(function (ContextBlock $block) {
|
||
$block->text('Customer #1234');
|
||
})
|
||
->sectionBlock(function (SectionBlock $block) {
|
||
$block->text('An invoice has been paid.');
|
||
})
|
||
->actionsBlock(function (ActionsBlock $block) {
|
||
$block->button('Acknowledge Invoice')
|
||
->primary()
|
||
->confirm(
|
||
'Acknowledge the payment and send a thank you email?',
|
||
function (ConfirmObject $dialog) {
|
||
$dialog->confirm('Yes');
|
||
$dialog->deny('No');
|
||
}
|
||
);
|
||
});
|
||
}
|
||
|
||
#### Inspecting Slack Blocks
|
||
|
||
If you would like to quickly inspect the blocks you've been building, you can
|
||
invoke the `dd` method on the `SlackMessage` instance. The `dd` method will
|
||
generate and dump a URL to Slack's [Block Kit
|
||
Builder](https://app.slack.com/block-kit-builder/), which displays a preview
|
||
of the payload and notification in your browser. You may pass `true` to the
|
||
`dd` method to dump the raw payload:
|
||
|
||
|
||
|
||
1return (new SlackMessage)
|
||
|
||
2 ->text('One of your invoices has been paid!')
|
||
|
||
3 ->headerBlock('Invoice Paid')
|
||
|
||
4 ->dd();
|
||
|
||
|
||
return (new SlackMessage)
|
||
->text('One of your invoices has been paid!')
|
||
->headerBlock('Invoice Paid')
|
||
->dd();
|
||
|
||
### Routing Slack Notifications
|
||
|
||
To direct Slack notifications to the appropriate Slack team and channel,
|
||
define a `routeNotificationForSlack` method on your notifiable model. This
|
||
method can return one of three values:
|
||
|
||
* `null` \- which defers routing to the channel configured in the notification itself. You may use the `to` method when building your `SlackMessage` to configure the channel within the notification.
|
||
* A string specifying the Slack channel to send the notification to, e.g. `#support-channel`.
|
||
* A `SlackRoute` instance, which allows you to specify an OAuth token and channel name, e.g. `SlackRoute::make($this->slack_channel, $this->slack_token)`. This method should be used to send notifications to external workspaces.
|
||
|
||
For instance, returning `#support-channel` from the
|
||
`routeNotificationForSlack` method will send the notification to the
|
||
`#support-channel` channel in the workspace associated with the Bot User OAuth
|
||
token located in your application's `services.php` configuration file:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
|
||
6use Illuminate\Notifications\Notifiable;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8
|
||
|
||
9class User extends Authenticatable
|
||
|
||
10{
|
||
|
||
11 use Notifiable;
|
||
|
||
12
|
||
|
||
13 /**
|
||
|
||
14 * Route notifications for the Slack channel.
|
||
|
||
15 */
|
||
|
||
16 public function routeNotificationForSlack(Notification $notification): mixed
|
||
|
||
17 {
|
||
|
||
18 return '#support-channel';
|
||
|
||
19 }
|
||
|
||
20}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use Notifiable;
|
||
|
||
/**
|
||
* Route notifications for the Slack channel.
|
||
*/
|
||
public function routeNotificationForSlack(Notification $notification): mixed
|
||
{
|
||
return '#support-channel';
|
||
}
|
||
}
|
||
|
||
### Notifying External Slack Workspaces
|
||
|
||
Before sending notifications to external Slack workspaces, your Slack App must
|
||
be distributed.
|
||
|
||
Of course, you will often want to send notifications to the Slack workspaces
|
||
owned by your application's users. To do so, you will first need to obtain a
|
||
Slack OAuth token for the user. Thankfully, [Laravel
|
||
Socialite](/docs/12.x/socialite) includes a Slack driver that will allow you
|
||
to easily authenticate your application's users with Slack and [obtain a bot
|
||
token](/docs/12.x/socialite#slack-bot-scopes).
|
||
|
||
Once you have obtained the bot token and stored it within your application's
|
||
database, you may utilize the `SlackRoute::make` method to route a
|
||
notification to the user's workspace. In addition, your application will
|
||
likely need to offer an opportunity for the user to specify which channel
|
||
notifications should be sent to:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Models;
|
||
|
||
4
|
||
|
||
5use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
|
||
6use Illuminate\Notifications\Notifiable;
|
||
|
||
7use Illuminate\Notifications\Notification;
|
||
|
||
8use Illuminate\Notifications\Slack\SlackRoute;
|
||
|
||
9
|
||
|
||
10class User extends Authenticatable
|
||
|
||
11{
|
||
|
||
12 use Notifiable;
|
||
|
||
13
|
||
|
||
14 /**
|
||
|
||
15 * Route notifications for the Slack channel.
|
||
|
||
16 */
|
||
|
||
17 public function routeNotificationForSlack(Notification $notification): mixed
|
||
|
||
18 {
|
||
|
||
19 return SlackRoute::make($this->slack_channel, $this->slack_token);
|
||
|
||
20 }
|
||
|
||
21}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Illuminate\Notifications\Notification;
|
||
use Illuminate\Notifications\Slack\SlackRoute;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use Notifiable;
|
||
|
||
/**
|
||
* Route notifications for the Slack channel.
|
||
*/
|
||
public function routeNotificationForSlack(Notification $notification): mixed
|
||
{
|
||
return SlackRoute::make($this->slack_channel, $this->slack_token);
|
||
}
|
||
}
|
||
|
||
## Localizing Notifications
|
||
|
||
Laravel allows you to send notifications in a locale other than the HTTP
|
||
request's current locale, and will even remember this locale if the
|
||
notification is queued.
|
||
|
||
To accomplish this, the `Illuminate\Notifications\Notification` class offers a
|
||
`locale` method to set the desired language. The application will change into
|
||
this locale when the notification is being evaluated and then revert back to
|
||
the previous locale when evaluation is complete:
|
||
|
||
|
||
|
||
1$user->notify((new InvoicePaid($invoice))->locale('es'));
|
||
|
||
|
||
$user->notify((new InvoicePaid($invoice))->locale('es'));
|
||
|
||
Localization of multiple notifiable entries may also be achieved via the
|
||
`Notification` facade:
|
||
|
||
|
||
|
||
1Notification::locale('es')->send(
|
||
|
||
2 $users, new InvoicePaid($invoice)
|
||
|
||
3);
|
||
|
||
|
||
Notification::locale('es')->send(
|
||
$users, new InvoicePaid($invoice)
|
||
);
|
||
|
||
#### User Preferred Locales
|
||
|
||
Sometimes, applications store each user's preferred locale. By implementing
|
||
the `HasLocalePreference` contract on your notifiable model, you may instruct
|
||
Laravel to use this stored locale when sending a notification:
|
||
|
||
|
||
|
||
1use Illuminate\Contracts\Translation\HasLocalePreference;
|
||
|
||
2
|
||
|
||
3class User extends Model implements HasLocalePreference
|
||
|
||
4{
|
||
|
||
5 /**
|
||
|
||
6 * Get the user's preferred locale.
|
||
|
||
7 */
|
||
|
||
8 public function preferredLocale(): string
|
||
|
||
9 {
|
||
|
||
10 return $this->locale;
|
||
|
||
11 }
|
||
|
||
12}
|
||
|
||
|
||
use Illuminate\Contracts\Translation\HasLocalePreference;
|
||
|
||
class User extends Model implements HasLocalePreference
|
||
{
|
||
/**
|
||
* Get the user's preferred locale.
|
||
*/
|
||
public function preferredLocale(): string
|
||
{
|
||
return $this->locale;
|
||
}
|
||
}
|
||
|
||
Once you have implemented the interface, Laravel will automatically use the
|
||
preferred locale when sending notifications and mailables to the model.
|
||
Therefore, there is no need to call the `locale` method when using this
|
||
interface:
|
||
|
||
|
||
|
||
1$user->notify(new InvoicePaid($invoice));
|
||
|
||
|
||
$user->notify(new InvoicePaid($invoice));
|
||
|
||
## Testing
|
||
|
||
You may use the `Notification` facade's `fake` method to prevent notifications
|
||
from being sent. Typically, sending notifications is unrelated to the code you
|
||
are actually testing. Most likely, it is sufficient to simply assert that
|
||
Laravel was instructed to send a given notification.
|
||
|
||
After calling the `Notification` facade's `fake` method, you may then assert
|
||
that notifications were instructed to be sent to users and even inspect the
|
||
data the notifications received:
|
||
|
||
Pest PHPUnit
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3use App\Notifications\OrderShipped;
|
||
|
||
4use Illuminate\Support\Facades\Notification;
|
||
|
||
5
|
||
|
||
6test('orders can be shipped', function () {
|
||
|
||
7 Notification::fake();
|
||
|
||
8
|
||
|
||
9 // Perform order shipping...
|
||
|
||
10
|
||
|
||
11 // Assert that no notifications were sent...
|
||
|
||
12 Notification::assertNothingSent();
|
||
|
||
13
|
||
|
||
14 // Assert a notification was sent to the given users...
|
||
|
||
15 Notification::assertSentTo(
|
||
|
||
16 [$user], OrderShipped::class
|
||
|
||
17 );
|
||
|
||
18
|
||
|
||
19 // Assert a notification was not sent...
|
||
|
||
20 Notification::assertNotSentTo(
|
||
|
||
21 [$user], AnotherNotification::class
|
||
|
||
22 );
|
||
|
||
23
|
||
|
||
24 // Assert a notification was sent twice...
|
||
|
||
25 Notification::assertSentTimes(WeeklyReminder::class, 2);
|
||
|
||
26
|
||
|
||
27 // Assert that a given number of notifications were sent...
|
||
|
||
28 Notification::assertCount(3);
|
||
|
||
29});
|
||
|
||
|
||
<?php
|
||
|
||
use App\Notifications\OrderShipped;
|
||
use Illuminate\Support\Facades\Notification;
|
||
|
||
test('orders can be shipped', function () {
|
||
Notification::fake();
|
||
|
||
// Perform order shipping...
|
||
|
||
// Assert that no notifications were sent...
|
||
Notification::assertNothingSent();
|
||
|
||
// Assert a notification was sent to the given users...
|
||
Notification::assertSentTo(
|
||
[$user], OrderShipped::class
|
||
);
|
||
|
||
// Assert a notification was not sent...
|
||
Notification::assertNotSentTo(
|
||
[$user], AnotherNotification::class
|
||
);
|
||
|
||
// Assert a notification was sent twice...
|
||
Notification::assertSentTimes(WeeklyReminder::class, 2);
|
||
|
||
// Assert that a given number of notifications were sent...
|
||
Notification::assertCount(3);
|
||
});
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace Tests\Feature;
|
||
|
||
4
|
||
|
||
5use App\Notifications\OrderShipped;
|
||
|
||
6use Illuminate\Support\Facades\Notification;
|
||
|
||
7use Tests\TestCase;
|
||
|
||
8
|
||
|
||
9class ExampleTest extends TestCase
|
||
|
||
10{
|
||
|
||
11 public function test_orders_can_be_shipped(): void
|
||
|
||
12 {
|
||
|
||
13 Notification::fake();
|
||
|
||
14
|
||
|
||
15 // Perform order shipping...
|
||
|
||
16
|
||
|
||
17 // Assert that no notifications were sent...
|
||
|
||
18 Notification::assertNothingSent();
|
||
|
||
19
|
||
|
||
20 // Assert a notification was sent to the given users...
|
||
|
||
21 Notification::assertSentTo(
|
||
|
||
22 [$user], OrderShipped::class
|
||
|
||
23 );
|
||
|
||
24
|
||
|
||
25 // Assert a notification was not sent...
|
||
|
||
26 Notification::assertNotSentTo(
|
||
|
||
27 [$user], AnotherNotification::class
|
||
|
||
28 );
|
||
|
||
29
|
||
|
||
30 // Assert a notification was sent twice...
|
||
|
||
31 Notification::assertSentTimes(WeeklyReminder::class, 2);
|
||
|
||
32
|
||
|
||
33 // Assert that a given number of notifications were sent...
|
||
|
||
34 Notification::assertCount(3);
|
||
|
||
35 }
|
||
|
||
36}
|
||
|
||
|
||
<?php
|
||
|
||
namespace Tests\Feature;
|
||
|
||
use App\Notifications\OrderShipped;
|
||
use Illuminate\Support\Facades\Notification;
|
||
use Tests\TestCase;
|
||
|
||
class ExampleTest extends TestCase
|
||
{
|
||
public function test_orders_can_be_shipped(): void
|
||
{
|
||
Notification::fake();
|
||
|
||
// Perform order shipping...
|
||
|
||
// Assert that no notifications were sent...
|
||
Notification::assertNothingSent();
|
||
|
||
// Assert a notification was sent to the given users...
|
||
Notification::assertSentTo(
|
||
[$user], OrderShipped::class
|
||
);
|
||
|
||
// Assert a notification was not sent...
|
||
Notification::assertNotSentTo(
|
||
[$user], AnotherNotification::class
|
||
);
|
||
|
||
// Assert a notification was sent twice...
|
||
Notification::assertSentTimes(WeeklyReminder::class, 2);
|
||
|
||
// Assert that a given number of notifications were sent...
|
||
Notification::assertCount(3);
|
||
}
|
||
}
|
||
|
||
You may pass a closure to the `assertSentTo` or `assertNotSentTo` methods in
|
||
order to assert that a notification was sent that passes a given "truth test".
|
||
If at least one notification was sent that passes the given truth test then
|
||
the assertion will be successful:
|
||
|
||
|
||
|
||
1Notification::assertSentTo(
|
||
|
||
2 $user,
|
||
|
||
3 function (OrderShipped $notification, array $channels) use ($order) {
|
||
|
||
4 return $notification->order->id === $order->id;
|
||
|
||
5 }
|
||
|
||
6);
|
||
|
||
|
||
Notification::assertSentTo(
|
||
$user,
|
||
function (OrderShipped $notification, array $channels) use ($order) {
|
||
return $notification->order->id === $order->id;
|
||
}
|
||
);
|
||
|
||
#### On-Demand Notifications
|
||
|
||
If the code you are testing sends on-demand notifications, you can test that
|
||
the on-demand notification was sent via the `assertSentOnDemand` method:
|
||
|
||
|
||
|
||
1Notification::assertSentOnDemand(OrderShipped::class);
|
||
|
||
|
||
Notification::assertSentOnDemand(OrderShipped::class);
|
||
|
||
By passing a closure as the second argument to the `assertSentOnDemand`
|
||
method, you may determine if an on-demand notification was sent to the correct
|
||
"route" address:
|
||
|
||
|
||
|
||
1Notification::assertSentOnDemand(
|
||
|
||
2 OrderShipped::class,
|
||
|
||
3 function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
|
||
|
||
4 return $notifiable->routes['mail'] === $user->email;
|
||
|
||
5 }
|
||
|
||
6);
|
||
|
||
|
||
Notification::assertSentOnDemand(
|
||
OrderShipped::class,
|
||
function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
|
||
return $notifiable->routes['mail'] === $user->email;
|
||
}
|
||
);
|
||
|
||
## Notification Events
|
||
|
||
#### Notification Sending Event
|
||
|
||
When a notification is sending, the
|
||
`Illuminate\Notifications\Events\NotificationSending` event is dispatched by
|
||
the notification system. This contains the "notifiable" entity and the
|
||
notification instance itself. You may create [event
|
||
listeners](/docs/12.x/events) for this event within your application:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Events\NotificationSending;
|
||
|
||
2
|
||
|
||
3class CheckNotificationStatus
|
||
|
||
4{
|
||
|
||
5 /**
|
||
|
||
6 * Handle the event.
|
||
|
||
7 */
|
||
|
||
8 public function handle(NotificationSending $event): void
|
||
|
||
9 {
|
||
|
||
10 // ...
|
||
|
||
11 }
|
||
|
||
12}
|
||
|
||
|
||
use Illuminate\Notifications\Events\NotificationSending;
|
||
|
||
class CheckNotificationStatus
|
||
{
|
||
/**
|
||
* Handle the event.
|
||
*/
|
||
public function handle(NotificationSending $event): void
|
||
{
|
||
// ...
|
||
}
|
||
}
|
||
|
||
The notification will not be sent if an event listener for the
|
||
`NotificationSending` event returns `false` from its `handle` method:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Handle the event.
|
||
|
||
3 */
|
||
|
||
4public function handle(NotificationSending $event): bool
|
||
|
||
5{
|
||
|
||
6 return false;
|
||
|
||
7}
|
||
|
||
|
||
/**
|
||
* Handle the event.
|
||
*/
|
||
public function handle(NotificationSending $event): bool
|
||
{
|
||
return false;
|
||
}
|
||
|
||
Within an event listener, you may access the `notifiable`, `notification`, and
|
||
`channel` properties on the event to learn more about the notification
|
||
recipient or the notification itself:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Handle the event.
|
||
|
||
3 */
|
||
|
||
4public function handle(NotificationSending $event): void
|
||
|
||
5{
|
||
|
||
6 // $event->channel
|
||
|
||
7 // $event->notifiable
|
||
|
||
8 // $event->notification
|
||
|
||
9}
|
||
|
||
|
||
/**
|
||
* Handle the event.
|
||
*/
|
||
public function handle(NotificationSending $event): void
|
||
{
|
||
// $event->channel
|
||
// $event->notifiable
|
||
// $event->notification
|
||
}
|
||
|
||
#### Notification Sent Event
|
||
|
||
When a notification is sent, the
|
||
`Illuminate\Notifications\Events\NotificationSent` [event](/docs/12.x/events)
|
||
is dispatched by the notification system. This contains the "notifiable"
|
||
entity and the notification instance itself. You may create [event
|
||
listeners](/docs/12.x/events) for this event within your application:
|
||
|
||
|
||
|
||
1use Illuminate\Notifications\Events\NotificationSent;
|
||
|
||
2
|
||
|
||
3class LogNotification
|
||
|
||
4{
|
||
|
||
5 /**
|
||
|
||
6 * Handle the event.
|
||
|
||
7 */
|
||
|
||
8 public function handle(NotificationSent $event): void
|
||
|
||
9 {
|
||
|
||
10 // ...
|
||
|
||
11 }
|
||
|
||
12}
|
||
|
||
|
||
use Illuminate\Notifications\Events\NotificationSent;
|
||
|
||
class LogNotification
|
||
{
|
||
/**
|
||
* Handle the event.
|
||
*/
|
||
public function handle(NotificationSent $event): void
|
||
{
|
||
// ...
|
||
}
|
||
}
|
||
|
||
Within an event listener, you may access the `notifiable`, `notification`,
|
||
`channel`, and `response` properties on the event to learn more about the
|
||
notification recipient or the notification itself:
|
||
|
||
|
||
|
||
1/**
|
||
|
||
2 * Handle the event.
|
||
|
||
3 */
|
||
|
||
4public function handle(NotificationSent $event): void
|
||
|
||
5{
|
||
|
||
6 // $event->channel
|
||
|
||
7 // $event->notifiable
|
||
|
||
8 // $event->notification
|
||
|
||
9 // $event->response
|
||
|
||
10}
|
||
|
||
|
||
/**
|
||
* Handle the event.
|
||
*/
|
||
public function handle(NotificationSent $event): void
|
||
{
|
||
// $event->channel
|
||
// $event->notifiable
|
||
// $event->notification
|
||
// $event->response
|
||
}
|
||
|
||
## Custom Channels
|
||
|
||
Laravel ships with a handful of notification channels, but you may want to
|
||
write your own drivers to deliver notifications via other channels. Laravel
|
||
makes it simple. To get started, define a class that contains a `send` method.
|
||
The method should receive two arguments: a `$notifiable` and a
|
||
`$notification`.
|
||
|
||
Within the `send` method, you may call methods on the notification to retrieve
|
||
a message object understood by your channel and then send the notification to
|
||
the `$notifiable` instance however you wish:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Notifications;
|
||
|
||
4
|
||
|
||
5use Illuminate\Notifications\Notification;
|
||
|
||
6
|
||
|
||
7class VoiceChannel
|
||
|
||
8{
|
||
|
||
9 /**
|
||
|
||
10 * Send the given notification.
|
||
|
||
11 */
|
||
|
||
12 public function send(object $notifiable, Notification $notification): void
|
||
|
||
13 {
|
||
|
||
14 $message = $notification->toVoice($notifiable);
|
||
|
||
15
|
||
|
||
16 // Send notification to the $notifiable instance...
|
||
|
||
17 }
|
||
|
||
18}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Notifications;
|
||
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class VoiceChannel
|
||
{
|
||
/**
|
||
* Send the given notification.
|
||
*/
|
||
public function send(object $notifiable, Notification $notification): void
|
||
{
|
||
$message = $notification->toVoice($notifiable);
|
||
|
||
// Send notification to the $notifiable instance...
|
||
}
|
||
}
|
||
|
||
Once your notification channel class has been defined, you may return the
|
||
class name from the `via` method of any of your notifications. In this
|
||
example, the `toVoice` method of your notification can return whatever object
|
||
you choose to represent voice messages. For example, you might define your own
|
||
`VoiceMessage` class to represent these messages:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace App\Notifications;
|
||
|
||
4
|
||
|
||
5use App\Notifications\Messages\VoiceMessage;
|
||
|
||
6use App\Notifications\VoiceChannel;
|
||
|
||
7use Illuminate\Bus\Queueable;
|
||
|
||
8use Illuminate\Contracts\Queue\ShouldQueue;
|
||
|
||
9use Illuminate\Notifications\Notification;
|
||
|
||
10
|
||
|
||
11class InvoicePaid extends Notification
|
||
|
||
12{
|
||
|
||
13 use Queueable;
|
||
|
||
14
|
||
|
||
15 /**
|
||
|
||
16 * Get the notification channels.
|
||
|
||
17 */
|
||
|
||
18 public function via(object $notifiable): string
|
||
|
||
19 {
|
||
|
||
20 return VoiceChannel::class;
|
||
|
||
21 }
|
||
|
||
22
|
||
|
||
23 /**
|
||
|
||
24 * Get the voice representation of the notification.
|
||
|
||
25 */
|
||
|
||
26 public function toVoice(object $notifiable): VoiceMessage
|
||
|
||
27 {
|
||
|
||
28 // ...
|
||
|
||
29 }
|
||
|
||
30}
|
||
|
||
|
||
<?php
|
||
|
||
namespace App\Notifications;
|
||
|
||
use App\Notifications\Messages\VoiceMessage;
|
||
use App\Notifications\VoiceChannel;
|
||
use Illuminate\Bus\Queueable;
|
||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||
use Illuminate\Notifications\Notification;
|
||
|
||
class InvoicePaid extends Notification
|
||
{
|
||
use Queueable;
|
||
|
||
/**
|
||
* Get the notification channels.
|
||
*/
|
||
public function via(object $notifiable): string
|
||
{
|
||
return VoiceChannel::class;
|
||
}
|
||
|
||
/**
|
||
* Get the voice representation of the notification.
|
||
*/
|
||
public function toVoice(object $notifiable): VoiceMessage
|
||
{
|
||
// ...
|
||
}
|
||
}
|
||
|