跳转至内容

通知

简介

除了支持发送电子邮件外,Laravel 还支持通过多种频道发送通知,包括电子邮件、短信(通过 Vonage,原名 Nexmo)和 Slack。此外,社区还创建了各种社区构建的通知频道,用于通过数十种不同的频道发送通知!通知还可以存储在数据库中,以便在您的 Web 界面中展示。

通常情况下,通知应该是简短的信息,告知用户应用程序中发生的事件。例如,如果您正在编写一个计费应用程序,您可能会通过电子邮件和短信频道向用户发送“发票已支付”通知。

生成通知

在 Laravel 中,每个通知都由一个类表示,通常存储在 app/Notifications 目录中。如果您的应用程序中没有此目录,不用担心——当您运行 make:notification Artisan 命令时,它会自动为您创建。

1php artisan make:notification InvoicePaid

此命令将在您的 app/Notifications 目录中放置一个新的通知类。每个通知类都包含一个 via 方法和若干个消息构建方法(例如 toMailtoDatabase),这些方法将通知转换为适合特定频道的消息。

发送通知

使用 Notifiable Trait

发送通知有两种方式:使用 Notifiable trait 的 notify 方法,或使用 Notification 门面Notifiable trait 默认包含在应用程序的 App\Models\User 模型中。

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}

此 trait 提供的 notify 方法期望接收一个通知实例。

1use App\Notifications\InvoicePaid;
2 
3$user->notify(new InvoicePaid($invoice));

请记住,您可以在任何模型上使用 Notifiable trait。您不仅限于在 User 模型上使用它。

使用 Notification 门面 (Facade)

或者,您可以通过 Notification 门面发送通知。当您需要向多个可通知实体(例如一组用户)发送通知时,此方法非常有用。要使用门面发送通知,请将所有可通知实体和通知实例传递给 send 方法。

1use Illuminate\Support\Facades\Notification;
2 
3Notification::send($users, new InvoicePaid($invoice));

您也可以使用 sendNow 方法立即发送通知。即使通知实现了 ShouldQueue 接口,此方法也会立即发送通知。

1Notification::sendNow($developers, new DeploymentCompleted($deployment));

指定发送频道

每个通知类都有一个 via 方法,该方法决定通知将通过哪些频道发送。通知可以通过 maildatabasebroadcastvonageslack 频道发送。

如果您想使用其他发送频道(例如 Telegram 或 Pusher),请查看社区驱动的 Laravel 通知频道网站

via 方法接收一个 $notifiable 实例,即发送该通知的目标类实例。您可以使用 $notifiable 来决定通知应通过哪些频道发送。

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}

队列化通知

在队列化通知之前,您应该配置队列并启动 worker

发送通知可能需要时间,特别是当频道需要进行外部 API 调用来发送通知时。为了加快应用程序的响应速度,可以通过将 ShouldQueue 接口和 Queueable trait 添加到类中来让通知排队。对于使用 make:notification 命令生成的通知,该接口和 trait 已经默认导入,因此您可以立即将它们添加到通知类中。

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}

一旦通知添加了 ShouldQueue 接口,您就可以像往常一样发送通知。Laravel 会检测到类上的 ShouldQueue 接口并自动将通知的发送加入队列。

1$user->notify(new InvoicePaid($invoice));

在排队通知时,将为每个接收者和频道组合创建一个排队的作业。例如,如果您的通知有三个接收者和两个频道,则会向队列分发六个作业。

延迟通知

如果您想延迟发送通知,可以在通知实例化时链式调用 delay 方法。

1$delay = now()->plus(minutes: 10);
2 
3$user->notify((new InvoicePaid($invoice))->delay($delay));

您可以将数组传递给 delay 方法,为特定频道指定延迟量。

1$user->notify((new InvoicePaid($invoice))->delay([
2 'mail' => now()->plus(minutes: 5),
3 'sms' => now()->plus(minutes: 10),
4]));

或者,您可以在通知类本身上定义 withDelay 方法。withDelay 方法应返回频道名称和延迟值的数组。

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()->plus(minutes: 5),
10 'sms' => now()->plus(minutes: 10),
11 ];
12}

自定义通知队列连接

默认情况下,排队的通知将使用应用程序的默认队列连接进行排队。如果您想为特定通知指定不同的连接,可以在通知的构造函数中调用 onConnection 方法。

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}

或者,如果您想为通知支持的每个通知频道指定特定的队列连接,可以在通知上定义 viaConnections 方法。此方法应返回频道名称 / 队列连接名称的数组。

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}

自定义通知频道队列

如果您想为通知支持的每个通知频道指定特定的队列,可以在通知上定义 viaQueues 方法。此方法应返回频道名称 / 队列名称的数组。

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}

自定义排队通知的作业属性

您可以通过在通知类上定义队列属性来自定义底层排队作业的行为。这些属性将被发送通知的排队作业继承。

1<?php
2 
3namespace App\Notifications;
4 
5use Illuminate\Bus\Queueable;
6use Illuminate\Contracts\Queue\ShouldQueue;
7use Illuminate\Notifications\Notification;
8use Illuminate\Queue\Attributes\MaxExceptions;
9use Illuminate\Queue\Attributes\Timeout;
10use Illuminate\Queue\Attributes\Tries;
11 
12#[Tries(5)]
13#[Timeout(120)]
14#[MaxExceptions(3)]
15class InvoicePaid extends Notification implements ShouldQueue
16{
17 use Queueable;
18 
19 // ...
20}

如果您想通过加密确保排队通知数据的隐私和完整性,请将 ShouldBeEncrypted 接口添加到您的通知类中。

1<?php
2 
3namespace App\Notifications;
4 
5use Illuminate\Bus\Queueable;
6use Illuminate\Contracts\Queue\ShouldBeEncrypted;
7use Illuminate\Contracts\Queue\ShouldQueue;
8use Illuminate\Notifications\Notification;
9 
10class InvoicePaid extends Notification implements ShouldQueue, ShouldBeEncrypted
11{
12 use Queueable;
13 
14 // ...
15}

除了直接在通知类上定义这些属性外,您还可以定义 backoffretryUntil 方法来指定排队通知作业的退避策略和重试超时时间。

1use DateTime;
2 
3/**
4 * Calculate the number of seconds to wait before retrying the notification.
5 */
6public function backoff(): int
7{
8 return 3;
9}
10 
11/**
12 * Determine the time at which the notification should timeout.
13 */
14public function retryUntil(): DateTime
15{
16 return now()->plus(minutes: 5);
17}

有关这些作业属性和方法的更多信息,请查看关于排队作业的文档。

排队通知中间件

排队通知可以像排队作业一样定义中间件。首先,在您的通知类上定义一个 middleware 方法。middleware 方法将接收 $notifiable$channel 变量,允许您根据通知的目标自定义返回的中间件。

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}

排队通知与数据库事务

当排队通知在数据库事务中被分发时,它们可能会在数据库事务提交之前被队列处理。发生这种情况时,您在数据库事务期间对模型或数据库记录所做的任何更新可能尚未反映在数据库中。此外,在事务中创建的任何模型或数据库记录可能在数据库中还不存在。如果您的通知依赖于这些模型,则在处理发送排队通知的作业时可能会出现意外错误。

如果您的队列连接的 after_commit 配置选项设置为 false,您仍然可以通过在发送通知时调用 afterCommit 方法,表明特定的排队通知应在所有打开的数据库事务提交后分发。

1use App\Notifications\InvoicePaid;
2 
3$user->notify((new InvoicePaid($invoice))->afterCommit());

或者,您可以从通知的构造函数中调用 afterCommit 方法。

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}

要了解更多关于解决这些问题的信息,请查看关于排队作业与数据库事务的文档。

确定是否应该发送排队通知

在排队通知被分发到队列进行后台处理后,它通常会被队列 worker 接收并发送给预期的接收者。

但是,如果您想在队列 worker 处理排队通知后,最终决定是否应该发送该通知,可以在通知类上定义一个 shouldSend 方法。如果此方法返回 false,则通知将不会发送。

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}

发送通知后

如果您想在通知发送后执行代码,可以定义一个 afterSending 方法。此方法将接收可通知实体、频道名称和来自频道的响应。

1/**
2 * Handle the notification after it has been sent.
3 */
4public function afterSending(object $notifiable, string $channel, mixed $response): void
5{
6 // ...
7}

按需通知 (On-Demand Notifications)

有时,您可能需要向未存储为应用程序“用户”的人发送通知。使用 Notification 门面的 route 方法,您可以在发送通知之前指定即时(ad-hoc)的通知路由信息。

1use Illuminate\Broadcasting\Channel;
2use Illuminate\Support\Facades\Notification;
3 
4Notification::route('mail', '[email protected]')
5 ->route('vonage', '5555555555')
6 ->route('slack', '#slack-channel')
7 ->route('broadcast', [new Channel('channel-name')])
8 ->notify(new InvoicePaid($invoice));

如果您想在向 mail 路由发送即时通知时提供收件人姓名,您可以提供一个数组,其中电子邮件地址作为键,姓名为第一个元素的值。

1Notification::route('mail', [
2 '[email protected]' => 'Barrett Blair',
3])->notify(new InvoicePaid($invoice));

使用 routes 方法,您可以一次为多个通知频道提供即时路由信息。

1Notification::routes([
2 'mail' => ['[email protected]' => 'Barrett Blair'],
3 'vonage' => '5555555555',
4])->notify(new InvoicePaid($invoice));

邮件通知

格式化邮件消息

如果通知支持以电子邮件形式发送,则应在通知类上定义 toMail 方法。此方法接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Messages\MailMessage 实例。

MailMessage 类包含几个简单的方法,帮助您构建事务性电子邮件。邮件消息可以包含文本行以及“号召性用语”(call to action)。让我们看一个 toMail 方法的例子。

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}

请注意,我们在 toMail 方法中使用了 $this->invoice->id。您可以将通知生成消息所需的任何数据传递给通知的构造函数。

在此示例中,我们注册了一个问候语、一行文本、一个号召性用语,然后是另一行文本。MailMessage 对象提供的这些方法使格式化小型事务性电子邮件变得简单快捷。邮件频道随后会将消息组件转换为精美的、响应式的 HTML 邮件模板,并附带纯文本版本。这是由 mail 频道生成的电子邮件示例。

发送邮件通知时,请确保在 config/app.php 配置文件中设置了 name 配置选项。此值将用于邮件通知消息的页眉和页脚。

错误消息

一些通知会告知用户错误,例如发票支付失败。您可以通过在构建消息时调用 error 方法来指示邮件消息涉及错误。当对邮件消息使用 error 方法时,号召性用语按钮将变为红色而不是黑色。

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}

其他邮件通知格式化选项

除了在通知类中定义文本“行”外,您还可以使用 view 方法来指定用于渲染通知邮件的自定义模板。

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}

您可以将视图名称作为传递给 view 方法的数组的第二个元素,从而为邮件消息指定纯文本视图。

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}

或者,如果您的消息仅具有纯文本视图,则可以使用 text 方法。

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}

自定义发件人

默认情况下,电子邮件的发送者/发件人地址是在 config/mail.php 配置文件中定义的。但是,您可以使用 from 方法为特定通知指定发件人地址。

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]', 'Barrett Blair')
8 ->line('...');
9}

自定义收件人

通过 mail 频道发送通知时,通知系统会自动在您的可通知实体上查找 email 属性。您可以通过在可通知实体上定义 routeNotificationForMail 方法来自定义用于发送通知的电子邮件地址。

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}

自定义主题

默认情况下,电子邮件的主题是格式化为“标题大小写”的通知类名称。因此,如果您的通知类名为 InvoicePaid,电子邮件的主题将为 Invoice Paid。如果您想为消息指定不同的主题,可以在构建消息时调用 subject 方法。

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}

自定义邮件驱动 (Mailer)

默认情况下,邮件通知将使用 config/mail.php 配置文件中定义的默认邮件驱动发送。但是,您可以通过在构建消息时调用 mailer 方法在运行时指定不同的邮件驱动。

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}

自定义模板

您可以通过发布通知包的资源来修改邮件通知使用的 HTML 和纯文本模板。运行此命令后,邮件通知模板将位于 resources/views/vendor/notifications 目录中。

1php artisan vendor:publish --tag=laravel-notifications

附件

要将附件添加到邮件通知中,请在构建消息时使用 attach 方法。attach 方法接受文件的绝对路径作为其第一个参数。

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}

通知邮件消息提供的 attach 方法也接受可附加对象。请查阅全面的可附加对象文档以了解更多信息。

将文件附加到消息时,您还可以通过将 array 作为 attach 方法的第二个参数传递来指定显示名称和/或 MIME 类型。

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}

与在 Mailable 对象中附加文件不同,您不能使用 attachFromStorage 直接从存储磁盘附加文件。您应该使用 attach 方法并提供存储磁盘上文件的绝对路径。或者,您可以从 toMail 方法返回一个 mailable

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}

必要时,可以使用 attachMany 方法将多个文件附加到消息中。

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}

原始数据附件

attachData 方法可用于将原始字节字符串作为附件附加。调用 attachData 方法时,应提供应分配给附件的文件名。

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}

添加标签和元数据

某些第三方电子邮件提供商(如 Mailgun 和 Postmark)支持消息“标签”和“元数据”,可用于对应用程序发送的电子邮件进行分组和跟踪。您可以通过 tagmetadata 方法将标签和元数据添加到电子邮件消息中。

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}

如果您的应用程序正在使用 Mailgun 驱动程序,您可以查阅 Mailgun 的文档以获取有关标签元数据的更多信息。同样,也可以查阅 Postmark 的文档,以获取有关其对标签元数据支持的更多信息。

如果您的应用程序正在使用 Amazon SES 发送电子邮件,则应使用 metadata 方法将 SES “标签”附加到消息中。

自定义 Symfony 消息

MailMessage 类的 withSymfonyMessage 方法允许您注册一个闭包,该闭包将在发送消息之前使用 Symfony 消息实例被调用。这使您有机会在消息发送之前对其进行深入自定义。

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}

使用 Mailable 类

如果需要,您可以从通知的 toMail 方法中返回一个完整的 mailable 对象。当返回 Mailable 而不是 MailMessage 时,您需要使用 mailable 对象的 to 方法指定消息收件人。

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}

Mailables 与即时通知

如果您正在发送即时通知,提供给 toMail 方法的 $notifiable 实例将是 Illuminate\Notifications\AnonymousNotifiable 的实例,它提供了一个 routeNotificationFor 方法,可用于检索应向其发送即时通知的电子邮件地址。

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}

预览邮件通知

设计邮件通知模板时,像普通 Blade 模板一样在浏览器中快速预览渲染后的邮件消息非常方便。因此,Laravel 允许您直接从路由闭包或控制器中返回邮件通知生成的任何邮件消息。当返回 MailMessage 时,它将在浏览器中渲染并显示,使您可以快速预览其设计,而无需将其发送到实际的电子邮件地址。

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});

Markdown 邮件通知

Markdown 邮件通知允许您利用邮件通知的预构建模板,同时让您更自由地编写更长、自定义的消息。由于消息是使用 Markdown 编写的,Laravel 能够为消息渲染精美的响应式 HTML 模板,同时自动生成纯文本版本。

生成消息

要生成带有相应 Markdown 模板的通知,可以使用 make:notification Artisan 命令的 --markdown 选项。

1php artisan make:notification InvoicePaid --markdown=mail.invoice.paid

与其他所有邮件通知一样,使用 Markdown 模板的通知应在其通知类上定义 toMail 方法。但是,不要使用 lineaction 方法来构建通知,而是使用 markdown 方法来指定应使用的 Markdown 模板名称。希望提供给模板的数据数组可以作为该方法的第二个参数传递。

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}

编写消息

Markdown 邮件通知使用 Blade 组件和 Markdown 语法的组合,使您能够轻松构建通知,同时利用 Laravel 预先设计的通知组件。

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>

在编写 Markdown 电子邮件时,请勿使用过多的缩进。根据 Markdown 标准,Markdown 解析器会将缩进的内容渲染为代码块。

按钮组件

按钮组件渲染一个居中的按钮链接。该组件接受两个参数:url 和可选的 color。支持的颜色为 primarygreenred。您可以根据需要向通知中添加任意数量的按钮组件。

1<x-mail::button :url="$url" color="green">
2View Invoice
3</x-mail::button>

面板组件

面板组件在面板中渲染给定的文本块,该面板的背景颜色与通知其余部分的背景颜色略有不同。这使您可以引起对给定文本块的注意。

1<x-mail::panel>
2This is the panel content.
3</x-mail::panel>

表格组件

表格组件允许您将 Markdown 表格转换为 HTML 表格。该组件接受 Markdown 表格作为其内容。支持使用默认的 Markdown 表格对齐语法进行表格列对齐。

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>

自定义组件

您可以将所有 Markdown 通知组件导出到您自己的应用程序中进行自定义。要导出组件,请使用 vendor:publish Artisan 命令发布 laravel-mail 资源标签。

1php artisan vendor:publish --tag=laravel-mail

此命令会将 Markdown 邮件组件发布到 resources/views/vendor/mail 目录。mail 目录将包含 htmltext 目录,每个目录都包含每个可用组件的相应表示。您可以随意自定义这些组件。

自定义 CSS

导出组件后,resources/views/vendor/mail/html/themes 目录将包含一个 default.css 文件。您可以自定义此文件中的 CSS,您的样式将自动内联到 Markdown 通知的 HTML 表示中。

如果您想为 Laravel 的 Markdown 组件构建全新的主题,可以将 CSS 文件放置在 html/themes 目录中。命名并保存 CSS 文件后,更新 mail 配置文件的 theme 选项以匹配新主题的名称。

要为单个通知自定义主题,可以在构建通知的邮件消息时调用 theme 方法。theme 方法接受发送通知时应使用的主题名称。

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}

数据库通知

前置条件

database 通知频道将通知信息存储在数据库表中。该表将包含诸如通知类型之类的信息,以及描述通知的 JSON 数据结构。

您可以查询该表以在应用程序的用户界面中显示通知。但是,在执行此操作之前,您需要创建一个数据库表来保存您的通知。您可以使用 make:notifications-table 命令生成具有正确表结构的迁移

1php artisan make:notifications-table
2 
3php artisan migrate

如果您的可通知模型正在使用 UUID 或 ULID 主键,则应在通知表迁移中将 morphs 方法替换为 uuidMorphsulidMorphs

格式化数据库通知

如果通知支持存储在数据库表中,则应在通知类上定义 toDatabasetoArray 方法。此方法将接收一个 $notifiable 实体,并应返回一个普通的 PHP 数组。返回的数组将被编码为 JSON 并存储在 notifications 表的 data 列中。让我们看一个 toArray 方法的例子。

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}

当通知存储在应用程序的数据库中时,type 列默认设置为通知的类名,read_at 列将为 null。但是,您可以通过在通知类中定义 databaseTypeinitialDatabaseReadAtValue 方法来自定义此行为。

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}

toDatabasetoArray

toArray 方法也由 broadcast 频道使用,以确定要广播到 JavaScript 前端的数据。如果您希望 databasebroadcast 频道有两种不同的数组表示,则应定义 toDatabase 方法而不是 toArray 方法。

访问通知

一旦通知存储在数据库中,您就需要一种方便的方式从可通知实体访问它们。Laravel 默认的 App\Models\User 模型中包含的 Illuminate\Notifications\Notifiable trait 包括一个 notifications Eloquent 关系,该关系返回该实体的通知。要获取通知,您可以像使用任何其他 Eloquent 关系一样访问此方法。默认情况下,通知将按 created_at 时间戳排序,最近的通知排在集合的前面。

1$user = App\Models\User::find(1);
2 
3foreach ($user->notifications as $notification) {
4 echo $notification->type;
5}

如果您只想检索“未读”通知,可以使用 unreadNotifications 关系。同样,这些通知将按 created_at 时间戳排序,最近的通知排在集合的前面。

1$user = App\Models\User::find(1);
2 
3foreach ($user->unreadNotifications as $notification) {
4 echo $notification->type;
5}

如果您只想检索“已读”通知,可以使用 readNotifications 关系。

1$user = App\Models\User::find(1);
2 
3foreach ($user->readNotifications as $notification) {
4 echo $notification->type;
5}

要从 JavaScript 客户端访问您的通知,您应该为您的应用程序定义一个通知控制器,该控制器返回可通知实体的通知(例如当前用户)。然后,您可以从 JavaScript 客户端向该控制器的 URL 发出 HTTP 请求。

标记通知为已读

通常,您希望在用户查看通知时将其标记为“已读”。Illuminate\Notifications\Notifiable trait 提供了一个 markAsRead 方法,该方法会更新通知数据库记录上的 read_at 列。

1$user = App\Models\User::find(1);
2 
3foreach ($user->unreadNotifications as $notification) {
4 $notification->markAsRead();
5}

但是,您无需循环遍历每个通知,而是可以直接在通知集合上使用 markAsRead 方法。

1$user->unreadNotifications->markAsRead();

您还可以使用批量更新查询来将所有通知标记为已读,而无需从数据库中检索它们。

1$user = App\Models\User::find(1);
2 
3$user->unreadNotifications()->update(['read_at' => now()]);

您可以 delete 通知以将其完全从表中移除。

1$user->notifications()->delete();

广播通知

前置条件

在广播通知之前,您应该配置并熟悉 Laravel 的事件广播服务。事件广播提供了一种从 JavaScript 前端响应服务器端 Laravel 事件的方法。

格式化广播通知

broadcast 频道使用 Laravel 的事件广播服务广播通知,允许您的 JavaScript 前端实时捕获通知。如果通知支持广播,则可以在通知类上定义 toBroadcast 方法。此方法接收一个 $notifiable 实体,并应返回一个 BroadcastMessage 实例。如果 toBroadcast 方法不存在,则将使用 toArray 方法来收集应广播的数据。返回的数据将被编码为 JSON 并广播到您的 JavaScript 前端。让我们看一个 toBroadcast 方法的例子。

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}

广播队列配置

所有广播通知都会加入队列以进行广播。如果您想配置用于广播操作的队列连接或队列名称,可以使用 BroadcastMessageonConnectiononQueue 方法。

1return (new BroadcastMessage($data))
2 ->onConnection('sqs')
3 ->onQueue('broadcasts');

自定义通知类型

除了您指定的数据外,所有广播通知都有一个 type 字段,其中包含通知的完整类名。如果您想自定义通知 type,可以在通知类上定义 broadcastType 方法。

1/**
2 * Get the type of the notification being broadcast.
3 */
4public function broadcastType(): string
5{
6 return 'broadcast.message';
7}

监听通知

通知将通过使用 {notifiable}.{id} 约定格式化的私有频道进行广播。因此,如果您要向 ID 为 1App\Models\User 实例发送通知,则通知将在 App.Models.User.1 私有频道上广播。使用 Laravel Echo 时,可以使用 notification 方法轻松监听频道上的通知。

1Echo.private('App.Models.User.' + userId)
2 .notification((notification) => {
3 console.log(notification.type);
4 });

使用 React 或 Vue

Laravel Echo 包含 React 和 Vue 钩子,使监听通知变得轻松。首先,调用 useEchoNotification 钩子来监听通知。当消费组件卸载时,useEchoNotification 钩子会自动退出频道。

1import { useEchoNotification } from "@laravel/echo-react";
2 
3useEchoNotification(
4 `App.Models.User.${userId}`,
5 (notification) => {
6 console.log(notification.type);
7 },
8);
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>

默认情况下,钩子会监听所有通知。要指定您想要监听的通知类型,可以向 useEchoNotification 提供字符串或类型数组。

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);
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>

您还可以指定通知载荷数据的形状,从而提供更高的类型安全性和编辑便利性。

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);

自定义通知频道

如果您想自定义实体广播通知的广播频道,可以在可通知实体上定义 receivesBroadcastNotificationsOn 方法。

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}

短信通知

前置条件

在 Laravel 中发送短信通知由 Vonage(原名 Nexmo)提供支持。在通过 Vonage 发送通知之前,您需要安装 laravel/vonage-notification-channelguzzlehttp/guzzle 包。

1composer require laravel/vonage-notification-channel guzzlehttp/guzzle

该包包含一个配置文件。但是,您无需将此配置文件导出到您自己的应用程序中。您只需使用 VONAGE_KEYVONAGE_SECRET 环境变量来定义您的 Vonage 公钥和私钥即可。

定义密钥后,您应设置一个 VONAGE_SMS_FROM 环境变量,该变量定义了默认发送短信消息的电话号码。您可以在 Vonage 控制面板中生成此电话号码。

1VONAGE_SMS_FROM=15556666666

格式化短信通知

如果通知支持以短信形式发送,则应在通知类上定义 toVonage 方法。此方法接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Messages\VonageMessage 实例。

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}

Unicode 内容

如果您的短信消息将包含 Unicode 字符,则在构建 VonageMessage 实例时应调用 unicode 方法。

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}

自定义“发件人”号码

如果您想从不同于 VONAGE_SMS_FROM 环境变量指定的电话号码发送某些通知,可以在 VonageMessage 实例上调用 from 方法。

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}

添加客户参考号

如果您想跟踪每个用户、团队或客户的成本,可以向通知添加“客户参考号”。Vonage 将允许您使用此客户参考号生成报告,以便您更好地了解特定客户的短信使用情况。客户参考号可以是最多 40 个字符的任何字符串。

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}

路由短信通知

要将 Vonage 通知路由到正确的电话号码,请在您的可通知实体上定义 routeNotificationForVonage 方法。

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}

Slack 通知

前置条件

在发送 Slack 通知之前,您应该通过 Composer 安装 Slack 通知频道。

1composer require laravel/slack-notification-channel

此外,您必须为您的 Slack 工作区创建一个 Slack App

如果您只需要向创建 App 的同一个 Slack 工作区发送通知,则应确保您的 App 拥有 chat:writechat:write.publicchat:write.customize 作用域。这些作用域可以从 Slack 中的“OAuth & Permissions”App 管理选项卡添加。

接下来,复制 App 的“Bot User OAuth Token”并将其放入应用程序 services.php 配置文件中的 slack 配置数组中。此令牌可以在 Slack 中的“OAuth & Permissions”选项卡上找到。

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],

App 分发

如果您的应用程序将向由您的应用程序用户拥有的外部 Slack 工作区发送通知,则需要通过 Slack “分发”您的 App。App 分发可以从 Slack 中 App 的“Manage Distribution”选项卡进行管理。一旦您的 App 被分发,您就可以使用 Socialite 代表您的应用程序用户获取 Slack Bot 令牌

格式化 Slack 通知

如果通知支持以 Slack 消息形式发送,则应在通知类上定义 toSlack 方法。此方法接收一个 $notifiable 实体,并应返回一个 Illuminate\Notifications\Slack\SlackMessage 实例。您可以使用 Slack 的 Block Kit API 构建丰富的通知。以下示例可以在 Slack 的 Block Kit builder 中进行预览。

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]")->markdown();
20 })
21 ->dividerBlock()
22 ->sectionBlock(function (SectionBlock $block) {
23 $block->text('Congratulations!');
24 });
25}

使用 Slack 的 Block Kit Builder 模板

除了使用流畅的消息构建器方法来构建您的 Block Kit 消息外,您还可以将 Slack 的 Block Kit Builder 生成的原始 JSON 载荷提供给 usingBlockKitTemplate 方法。

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}

Slack 交互性

Slack 的 Block Kit 通知系统提供了强大的功能来处理用户交互。要利用这些功能,您的 Slack App 应启用“Interactivity”,并配置一个指向您的应用程序提供的 URL 的“Request URL”。这些设置可以从 Slack 中 App 的“Interactivity & Shortcuts”管理选项卡进行管理。

在以下使用 actionsBlock 方法的示例中,Slack 将向您的“Request URL”发送一个 POST 请求,其中包含点击按钮的 Slack 用户、点击按钮的 ID 等载荷。然后,您的应用程序可以根据载荷确定要采取的行动。您还应该验证该请求是由 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}

确认模态

如果您希望用户在执行操作前必须确认操作,可以在定义按钮时调用 confirm 方法。confirm 方法接受消息和一个接收 ConfirmObject 实例的闭包。

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}

检查 Slack Blocks

如果您想快速检查您一直在构建的块,可以在 SlackMessage 实例上调用 dd 方法。dd 方法将生成并 dump 一个 URL 到 Slack 的 Block Kit Builder,它会在浏览器中显示载荷和通知的预览。您可以将 true 传递给 dd 方法以 dump 原始载荷。

1return (new SlackMessage)
2 ->text('One of your invoices has been paid!')
3 ->headerBlock('Invoice Paid')
4 ->dd();

路由 Slack 通知

要将 Slack 通知定向到适当的 Slack 团队和频道,请在您的可通知模型上定义 routeNotificationForSlack 方法。此方法可以返回三个值之一:

  • null - 将路由推迟到通知本身中配置的频道。您可以使用 SlackMessage 构建时的 to 方法来配置通知内的频道。
  • 指定要向其发送通知的 Slack 频道的字符串,例如 #support-channel
  • SlackRoute 实例,它允许您指定 OAuth 令牌和频道名称,例如 SlackRoute::make($this->slack_channel, $this->slack_token)。此方法应用于向外部工作区发送通知。

例如,从 routeNotificationForSlack 方法返回 #support-channel 将把通知发送到与应用程序 services.php 配置文件中定义的 Bot User OAuth 令牌关联的工作区中的 #support-channel 频道。

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}

通知外部 Slack 工作区

在向外部 Slack 工作区发送通知之前,您的 Slack App 必须被分发

当然,您通常会希望向您的应用程序用户拥有的 Slack 工作区发送通知。为此,您首先需要获取用户的 Slack OAuth 令牌。值得庆幸的是,Laravel Socialite 包含一个 Slack 驱动程序,允许您轻松地使用 Slack 对应用程序用户进行身份验证并获取 Bot 令牌

一旦您获得了 Bot 令牌并将其存储在应用程序的数据库中,您就可以使用 SlackRoute::make 方法将通知路由到用户的工作区。此外,您的应用程序可能需要提供机会让用户指定应将通知发送到哪个频道。

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}

本地化通知

Laravel 允许您以 HTTP 请求当前区域设置以外的区域设置发送通知,如果通知是排队的,它甚至会记住此区域设置。

为此,Illuminate\Notifications\Notification 类提供了一个 locale 方法来设置所需的语言。当评估通知时,应用程序将切换到此区域设置,并在评估完成后还原回之前的区域设置。

1$user->notify((new InvoicePaid($invoice))->locale('es'));

多个可通知实体的本地化也可以通过 Notification 门面实现。

1Notification::locale('es')->send(
2 $users, new InvoicePaid($invoice)
3);

用户首选区域设置

有时,应用程序会存储每个用户的首选区域设置。通过在可通知模型上实现 HasLocalePreference 契约,您可以指示 Laravel 在发送通知时使用此存储的区域设置。

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}

一旦实现了该接口,Laravel 在向模型发送通知和 mailable 时将自动使用首选区域设置。因此,使用此接口时无需调用 locale 方法。

1$user->notify(new InvoicePaid($invoice));

测试

您可以使用 Notification 门面的 fake 方法来防止发送通知。通常,发送通知与您实际测试的代码无关。很可能,只需断言 Laravel 已被指示发送给定的通知就足够了。

调用 Notification 门面的 fake 方法后,您可以断言已指示将通知发送给用户,甚至可以检查通知收到的数据。

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});
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}

您可以将闭包传递给 assertSentToassertNotSentTo 方法,以断言发送了一个通过给定“真值测试”的通知。如果至少有一个通知发送并通过了给定的真值测试,则断言将成功。

1Notification::assertSentTo(
2 $user,
3 function (OrderShipped $notification, array $channels) use ($order) {
4 return $notification->order->id === $order->id;
5 }
6);

按需通知 (On-Demand Notifications)

如果您测试的代码发送了即时通知,则可以通过 assertSentOnDemand 方法测试是否发送了即时通知。

1Notification::assertSentOnDemand(OrderShipped::class);

通过将闭包作为 assertSentOnDemand 方法的第二个参数传递,您可以确定是否向正确的“路由”地址发送了即时通知。

1Notification::assertSentOnDemand(
2 OrderShipped::class,
3 function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
4 return $notifiable->routes['mail'] === $user->email;
5 }
6);

通知事件

通知发送事件

当通知正在发送时,Illuminate\Notifications\Events\NotificationSending 事件由通知系统分发。这包含“可通知”实体和通知实例本身。您可以在应用程序中为此事件创建事件监听器

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}

如果 NotificationSending 事件的事件监听器从其 handle 方法返回 false,则不会发送通知。

1/**
2 * Handle the event.
3 */
4public function handle(NotificationSending $event): bool
5{
6 return false;
7}

在事件监听器内,您可以访问事件上的 notifiablenotificationchannel 属性,以了解有关通知接收者或通知本身的更多信息。

1/**
2 * Handle the event.
3 */
4public function handle(NotificationSending $event): void
5{
6 // $event->channel
7 // $event->notifiable
8 // $event->notification
9}

通知已发送事件

当通知已发送时,Illuminate\Notifications\Events\NotificationSent 事件由通知系统分发。这包含“可通知”实体和通知实例本身。您可以在应用程序中为此事件创建事件监听器

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}

在事件监听器内,您可以访问事件上的 notifiablenotificationchannelresponse 属性,以了解有关通知接收者或通知本身的更多信息。

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}

自定义频道

Laravel 自带少量通知频道,但您可能想编写自己的驱动程序来通过其他频道发送通知。Laravel 使其变得简单。首先,定义一个包含 send 方法的类。该方法应接收两个参数:$notifiable$notification

send 方法中,您可以调用通知上的方法来检索您的频道理解的消息对象,然后以任何您希望的方式将通知发送给 $notifiable 实例。

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}

一旦定义了通知频道类,您就可以从任何通知的 via 方法返回该类名。在此示例中,通知的 toVoice 方法可以返回您选择用来表示语音消息的任何对象。例如,您可以定义自己的 VoiceMessage 类来表示这些消息。

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}