通知
生成通知
在 Laravel 中,每个通知由一个单独的类表示,通常存储在 app/Notifications
目录中。如果您在应用程序中没有看到这个目录,不用担心——当您运行 make:notification
Artisan 命令时,Laravel 会为您创建这个目录:
php artisan make:notification InvoicePaid
此命令将在 app/Notifications
目录中创建一个新的通知类。每个通知类包含一个 via
方法和多个消息构建方法,例如 toMail
或 toDatabase
,这些方法将通知转换为适合特定渠道的消息。
发送通知
使用 Notifiable
特性
通知可以通过两种方式发送:使用 Notifiable
特性中的 notify
方法,或使用 Notification
facade。Notifiable
特性默认已包含在应用程序的 App\Models\User
模型中:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
}
notify
方法由该特性提供,期望接收一个通知实例:
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
请记住,您可以将 |
使用 Notification
facade
另外,您也可以通过 Notification
facade 发送通知。这种方法在您需要将通知发送给多个可通知实体(例如一组用户)时非常有用。使用 facade 发送通知时,将所有可通知实体和通知实例传递给 send
方法:
use Illuminate\Support\Facades\Notification;
Notification::send($users, new InvoicePaid($invoice));
您还可以使用 sendNow
方法立即发送通知。即使通知实现了 ShouldQueue
接口,该方法也会立即发送通知:
Notification::sendNow($developers, new DeploymentCompleted($deployment));
指定投递渠道
每个通知类都有一个 via
方法,用于确定通知将通过哪些渠道发送。通知可以通过邮件、数据库、广播、Vonage 和 Slack 渠道发送。
如果您想使用其它传送渠道(例如 Telegram 或 Pusher),可以查看由社区驱动的 Laravel Notification Channels 网站。 |
via
方法接收一个 $notifiable
实例,该实例是通知发送对象的类实例。您可以使用 $notifiable
来确定通知应该通过哪些渠道发送:
/**
* 获取通知的传送渠道。
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];
}
排队通知
在将通知排入队列之前,您应该配置您的队列并【启动一个工作者】。 |
发送通知可能需要一些时间,尤其是当渠道需要调用外部 API 来发送通知时。为了加快应用程序的响应时间,您可以通过在通知类中添加 ShouldQueue
接口和 Queueable
特性来将通知排入队列。所有通过 make:notification
命令生成的通知默认已经导入了这些接口和特性,因此您可以直接将它们添加到您的通知类中:
<?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;
// ...
}
一旦 ShouldQueue
接口被添加到您的通知中,您可以像正常一样发送通知。Laravel 会检测类中的 ShouldQueue
接口并自动将通知的发送排入队列:
$user->notify(new InvoicePaid($invoice));
在排队通知时,对于每个收件人和渠道的组合,都会创建一个排队作业。例如,如果您的通知有三个收件人和两个渠道,则会有六个作业被派发到队列中。
延迟通知
如果您希望延迟通知的发送,可以将 delay
方法链接到通知的实例化过程中:
$delay = now()->addMinutes(10);
$user->notify((new InvoicePaid($invoice))->delay($delay));
您可以将一个数组传递给 delay
方法,为特定渠道指定延迟时间:
$user->notify((new InvoicePaid($invoice))->delay([
'mail' => now()->addMinutes(5),
'sms' => now()->addMinutes(10),
]));
另外,您还可以在通知类中定义一个 withDelay
方法。该方法应返回一个包含渠道名和延迟时间的数组:
/**
* 确定通知的传送延迟。
*
* @return array<string, \Illuminate\Support\Carbon>
*/
public function withDelay(object $notifiable): array
{
return [
'mail' => now()->addMinutes(5),
'sms' => now()->addMinutes(10),
];
}
自定义通知队列连接
默认情况下,排队通知将使用应用程序的默认队列连接。如果您想为特定的通知指定不同的连接,可以在通知的构造函数中调用 onConnection
方法:
<?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;
/**
* 创建一个新的通知实例。
*/
public function __construct()
{
$this->onConnection('redis');
}
}
或者,如果您希望为每个通知支持的通知渠道指定特定的队列连接,您可以在通知中定义一个 viaConnections
方法。该方法应返回一个包含渠道名/队列连接名对的数组:
/**
* 确定每个通知渠道应使用的连接。
*
* @return array<string, string>
*/
public function viaConnections(): array
{
return [
'mail' => 'redis',
'database' => 'sync',
];
}
自定义通知渠道队列
如果您希望为每个通知支持的通知渠道指定特定的队列,可以在通知中定义一个 viaQueues
方法。该方法应返回一个包含渠道名/队列名对的数组:
/**
* 确定每个通知渠道应使用的队列。
*
* @return array<string, string>
*/
public function viaQueues(): array
{
return [
'mail' => 'mail-queue',
'slack' => 'slack-queue',
];
}
排队通知中间件
排队通知可以像排队作业一样定义中间件。要开始使用,您可以在通知类中定义一个 middleware
方法。该方法将接收 $notifiable
和 $channel
变量,这样您就可以根据通知的目标自定义返回的中间件:
use Illuminate\Queue\Middleware\RateLimited;
/**
* 获取通知作业应通过的中间件。
*
* @return array<int, object>
*/
public function middleware(object $notifiable, string $channel)
{
return match ($channel) {
'email' => [new RateLimited('postmark')],
'slack' => [new RateLimited('slack')],
default => [],
};
}
排队通知和数据库事务
当排队通知在数据库事务中被派发时,它们可能会在数据库事务提交之前被队列处理。发生这种情况时,您在数据库事务中所做的任何模型或数据库记录的更新可能还未反映在数据库中。此外,在事务中创建的任何模型或数据库记录可能在数据库中不存在。如果您的通知依赖于这些模型,可能会在处理排队通知的作业时发生意外错误。
如果您的队列连接的 after_commit
配置选项设置为 false
,您仍然可以通过在发送通知时调用 afterCommit
方法,指示某个特定的排队通知应该在所有打开的数据库事务提交后派发:
use App\Notifications\InvoicePaid;
$user->notify((new InvoicePaid($invoice))->afterCommit());
或者,您可以在通知的构造函数中调用 afterCommit
方法:
<?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;
/**
* 创建一个新的通知实例。
*/
public function __construct()
{
$this->afterCommit();
}
}
要了解更多关于如何解决这些问题,请查看关于【排队作业和数据库事务】的文档。 |
按需通知
有时,您可能需要将通知发送给未作为应用程序 “用户” 存储的人员。通过使用 Notification facade 的 route
方法,您可以在发送通知之前指定临时的通知路由信息:
use Illuminate\Broadcasting\Channel;
use Illuminate\Support\Facades\Notification;
Notification::route('mail', 'taylor@example.com')
->route('vonage', '5555555555')
->route('slack', '#slack-channel')
->route('broadcast', [new Channel('channel-name')])
->notify(new InvoicePaid($invoice));
如果您想在发送即时通知到邮件路由时提供接收者的名字,可以提供一个包含电子邮件地址作为键,姓名作为值的数组,第一个元素为该数组的值:
Notification::route('mail', [
'barrett@example.com' => 'Barrett Blair',
])->notify(new InvoicePaid($invoice));
使用 routes
方法,您可以一次性为多个通知渠道提供临时的路由信息:
Notification::routes([
'mail' => ['barrett@example.com' => 'Barrett Blair'],
'vonage' => '5555555555',
])->notify(new InvoicePaid($invoice));
邮件通知
格式化邮件消息
如果一个通知支持作为电子邮件发送,您应该在通知类中定义一个 toMail
方法。该方法将接收一个 $notifiable
实体,并应返回一个 Illuminate\Notifications\Messages\MailMessage
实例。
MailMessage
类包含一些简单的方法,帮助您构建事务性电子邮件消息。邮件消息可以包含文本行以及一个“行动号召”。让我们看看一个 toMail
方法的示例:
/**
* 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!');
}
注意,我们在 |
在这个示例中,我们注册了一个问候语、一行文本、一个行动号召,然后是另一行文本。MailMessage
对象提供的这些方法使得格式化小型事务性电子邮件变得简单快捷。邮件频道将把消息组件转换成一个美观、响应式的 HTML 邮件模板,并带有纯文本版本。以下是通过邮件频道生成的电子邮件示例:

发送邮件通知时,请确保在 |
错误消息
有些通知是用来通知用户错误的,例如发票支付失败。您可以通过在构建邮件消息时调用 error
方法,表明邮件消息是关于错误的。使用 error
方法时,操作按钮将变为红色而非黑色:
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->error()
->subject('Invoice Payment Failed')
->line('...');
}
其它邮件通知格式化选项
您可以通过 view
方法指定一个自定义模板,用于呈现通知邮件,而不是在通知类中定义文本行:
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->view(
'mail.invoice.paid', ['invoice' => $this->invoice]
);
}
您还可以为邮件消息指定一个纯文本视图,方法是将视图名称作为数组的第二个元素传递给 view
方法:
/**
* 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]
);
}
或者,如果您的消息只有纯文本视图,可以使用 text
方法:
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)->text(
'mail.invoice.paid-text', ['invoice' => $this->invoice]
);
}
自定义发件人
默认情况下,电子邮件的发件人地址是通过 config/mail.php
配置文件定义的。不过,您可以通过 from
方法为特定的通知指定发件人地址:
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->from('barrett@example.com', 'Barrett Blair')
->line('...');
}
自定义收件人
在通过邮件频道发送通知时,通知系统会自动查找您的可通知实体上的 email
属性。您可以通过在可通知实体上定义 routeNotificationForMail
方法来定制用于发送通知的电子邮件地址:
<?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 $this->email_address;
// 返回电子邮件地址和姓名...
return [$this->email_address => $this->name];
}
}
自定义主题
默认情况下,电子邮件的主题是通知类的名称,格式化为 “标题大小写”。因此,如果您的通知类名为 InvoicePaid
,则电子邮件的主题将是 Invoice Paid
。如果您希望为消息指定不同的主题,可以在构建消息时调用 subject
方法:
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('通知主题')
->line('...');
}
自定义邮件发送器
默认情况下,电子邮件通知将使用在 config/mail.php
配置文件中定义的默认邮件发送器。然而,您可以通过在构建消息时调用 mailer
方法,在运行时指定不同的邮件发送器:
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->mailer('postmark')
->line('...');
}
自定义模板
您可以通过发布通知包的资源来修改邮件通知使用的 HTML 和纯文本模板。在运行此命令后,邮件通知模板将位于 resources/views/vendor/notifications
目录中:
php artisan vendor:publish --tag=laravel-notifications
附件
要向电子邮件通知添加附件,可以在构建邮件消息时使用 attach
方法。attach
方法的第一个参数是文件的绝对路径:
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attach('/path/to/file');
}
通知邮件消息提供的 |
在附加文件时,您还可以通过将一个数组作为第二个参数传递给 attach
方法,来指定显示名称和/或 MIME 类型:
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('Hello!')
->attach('/path/to/file', [
'as' => 'name.pdf',
'mime' => 'application/pdf',
]);
}
与在 Mailable 对象中附加文件不同,您不能直接使用 attachFromStorage
从存储磁盘附加文件。您应该使用 attach
方法,并提供文件在存储磁盘上的绝对路径。或者,您可以在 toMail
方法中返回一个 Mailable:
use App\Mail\InvoicePaid as InvoicePaidMailable;
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): Mailable
{
return (new InvoicePaidMailable($this->invoice))
->to($notifiable->email)
->attachFromStorage('/path/to/file');
}
如果需要,可以使用 attachMany
方法将多个文件附加到消息中:
/**
* 获取通知的邮件表示。
*/
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',
],
]);
}
添加标签和元数据
一些第三方电子邮件提供商,如 Mailgun 和 Postmark,支持邮件 “标签” 和 “元数据”,这些可以用来对您的应用程序发送的邮件进行分组和追踪。您可以通过 tag
和 metadata
方法向电子邮件消息添加标签和元数据:
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->greeting('评论已被点赞!')
->tag('upvote')
->metadata('comment_id', $this->comment->id);
}
如果您的应用程序使用 Amazon SES 发送电子邮件,您应该使用 metadata
方法将 SES 的 “标签” 附加到消息中。
自定义 Symfony 消息
MailMessage
类的 withSymfonyMessage
方法允许您注册一个闭包,该闭包将在发送邮件之前与 Symfony 的 Message
实例一起调用。这使您有机会在邮件发送前深入定制邮件内容:
use Symfony\Component\Mime\Email;
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->withSymfonyMessage(function (Email $message) {
$message->getHeaders()->addTextHeader(
'Custom-Header', 'Header Value'
);
});
}
这个方法使您能够添加自定义的邮件头或进行其它高级自定义操作,确保邮件在发送之前满足您的需求。
使用 Mailables
如果需要,您可以从通知的 toMail
方法返回一个完整的 mailable 对象。当返回一个 Mailable
而不是 MailMessage
时,您需要使用 mailable 对象的 to
方法指定邮件的接收者:
use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Mail\Mailable;
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): Mailable
{
return (new InvoicePaidMailable($this->invoice))
->to($notifiable->email);
}
Mailables 和按需通知
如果您正在发送按需通知,那么传递给 toMail
方法的 $notifiable
实例将是 Illuminate\Notifications\AnonymousNotifiable
类型,后者提供了一个 routeNotificationFor
方法,可用于获取按需通知应该发送到的电子邮件地址:
use App\Mail\InvoicePaid as InvoicePaidMailable;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Mail\Mailable;
/**
* 获取通知的邮件表示。
*/
public function toMail(object $notifiable): Mailable
{
$address = $notifiable instanceof AnonymousNotifiable
? $notifiable->routeNotificationFor('mail')
: $notifiable->email;
return (new InvoicePaidMailable($this->invoice))
->to($address);
}
预览邮件通知
在设计邮件通知模板时,能够像典型的 Blade 模板一样快速预览渲染后的邮件消息非常方便。为此,Laravel 允许您直接从路由闭包或控制器返回任何由邮件通知生成的邮件消息。当返回一个 MailMessage
时,它将被渲染并显示在浏览器中,让您能够快速预览其设计,而无需将其发送到实际的电子邮件地址:
use App\Models\Invoice;
use App\Notifications\InvoicePaid;
Route::get('/notification', function () {
$invoice = Invoice::find(1);
return (new InvoicePaid($invoice))
->toMail($invoice->user);
});
Markdown 邮件通知
Markdown 邮件通知允许您利用邮件通知的预构建模板,同时提供更大的自由度来编写更长、更自定义的消息。由于消息是用 Markdown 编写的,Laravel 可以为这些消息渲染美观、响应式的 HTML 模板,并自动生成一个纯文本版本。
生成消息
要生成具有相应 Markdown 模板的通知,您可以使用 make:notification
Artisan 命令的 --markdown
选项:
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
与所有其它邮件通知一样,使用 Markdown 模板的通知应在其通知类中定义 toMail
方法。然而,您应该使用 markdown
方法来指定应该使用的 Markdown 模板的名称,而不是使用 line
和 action
方法来构建通知。您可以通过方法的第二个参数传递一个数据数组,以便在模板中使用:
/**
* 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]);
}
编写消息
Markdown 邮件通知使用 Blade 组件和 Markdown 语法的组合,使您能够轻松构建通知,同时利用 Laravel 的预先制作的通知组件:
<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>
按钮组件
按钮组件呈现一个居中的按钮链接。该组件接受两个参数:一个 URL 和一个可选的颜色。支持的颜色有 primary
、green
和 red
。您可以在通知中添加任意数量的按钮组件:
<x-mail::button :url="$url" color="green">
View Invoice
</x-mail::button>
自定义组件
您可以将所有 Markdown 邮件通知组件导出到您的应用程序中进行自定义。要导出组件,请使用 vendor:publish
Artisan 命令发布 laravel-mail
资产标签:
php artisan vendor:publish --tag=laravel-mail
此命令将把 Markdown 邮件组件发布到 resources/views/vendor/mail
目录。mail
目录将包含 html
和 text
目录,每个目录都包含每个可用组件的相应表示形式。您可以根据需要自定义这些组件。
自定义 CSS
导出组件后,resources/views/vendor/mail/html/themes
目录将包含一个 default.css
文件。您可以自定义此文件中的 CSS,您的样式将自动内联到 Markdown 通知的 HTML 表示中。
如果您想为 Laravel 的 Markdown 组件构建一个全新的主题,您可以将 CSS 文件放置在 html/themes
目录中。命名并保存您的 CSS 文件后,更新邮件配置文件中的 theme
选项,将其设置为您的新主题的名称。
要为单个通知自定义主题,您可以在构建通知的邮件消息时调用 theme
方法。theme
方法接受一个字符串参数,即要在发送通知时使用的主题名称:
/**
* 获取邮件表示的通知。
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->theme('invoice')
->subject('Invoice Paid')
->markdown('mail.invoice.paid', ['url' => $url]);
}
数据库通知
前提条件
数据库通知通道将通知信息存储在数据库表中。该表将包含通知类型以及描述通知的 JSON 数据结构等信息。
您可以查询该表,以便在应用程序的用户界面中显示通知。但在此之前,您需要创建一个数据库表来存储通知。您可以使用 make:notifications-table
命令生成一个带有适当表结构的迁移文件:
php artisan make:notifications-table
然后运行迁移:
php artisan migrate
如果您的可通知模型使用 【UUID 或 ULID 作为主键】,则应在通知表的迁移中将 |
格式化数据库通知
如果一个通知支持存储在数据库表中,您应该在通知类中定义 toDatabase
或 toArray
方法。这个方法将接收一个 $notifiable
实体,并应该返回一个普通的 PHP 数组。返回的数组将被编码为 JSON 并存储在通知表的 data
列中。以下是一个 toArray
方法的示例:
/**
* 获取通知的数组表示。
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
当通知存储在应用程序的数据库中时,type
列将被填充为通知的类名。然而,您可以通过在通知类中定义一个 databaseType
方法来定制此行为:
/**
* 获取通知的数据库类型。
*
* @return string
*/
public function databaseType(object $notifiable): string
{
return 'invoice-paid';
}
访问通知
一旦通知存储在数据库中,您需要一个方便的方式来从您的可通知实体中访问它们。Laravel 的 Illuminate\Notifications\Notifiable
特性(该特性默认包含在 App\Models\User
模型中)提供了一个 notifications
Eloquent 关系,返回该实体的所有通知。您可以像访问其它 Eloquent 关系一样访问这个方法。默认情况下,通知将按 created_at
时间戳排序,最新的通知会排在集合的前面:
$user = App\Models\User::find(1);
foreach ($user->notifications as $notification) {
echo $notification->type;
}
如果您只想检索 “未读” 通知,可以使用 unreadNotifications
关系。同样,这些通知将按 created_at
时间戳排序,最新的通知会排在集合的前面:
$user = App\Models\User::find(1);
foreach ($user->unreadNotifications as $notification) {
echo $notification->type;
}
为了从 JavaScript 客户端访问通知,您应该为应用程序定义一个通知控制器,该控制器返回一个可通知实体(例如当前用户)的通知。然后,您可以从 JavaScript 客户端向该控制器的 URL 发起 HTTP 请求。 |
标记通知为已读
通常,当用户查看通知时,您会希望将其标记为 “已读”。Illuminate\Notifications\Notifiable
特性提供了一个 markAsRead
方法,用于更新通知数据库记录中的 read_at
列:
$user = App\Models\User::find(1);
foreach ($user->unreadNotifications as $notification) {
$notification->markAsRead();
}
但是,您也可以直接在通知集合上使用 markAsRead
方法,而无需遍历每个通知:
$user->unreadNotifications->markAsRead();
您还可以使用批量更新查询,将所有通知标记为已读,而无需从数据库中检索它们:
$user = App\Models\User::find(1);
$user->unreadNotifications()->update(['read_at' => now()]);
如果您希望 delete
通知,以便将其完全从表中移除,可以使用以下方法:
$user->notifications()->delete();
广播通知
格式化广播通知
广播频道使用 Laravel 的事件广播服务来广播通知,使您的 JavaScript 驱动前端能够实时接收通知。如果通知支持广播,您可以在通知类中定义一个 toBroadcast
方法。该方法将接收一个 $notifiable
实体,并应返回一个 BroadcastMessage
实例。如果 toBroadcast
方法不存在,则会使用 toArray
方法来收集应该广播的数据。返回的数据将被编码为 JSON,并广播到您的 JavaScript 驱动前端。以下是一个示例 toBroadcast
方法:
use Illuminate\Notifications\Messages\BroadcastMessage;
/**
* 获取可广播的通知表示形式。
*/
public function toBroadcast(object $notifiable): BroadcastMessage
{
return new BroadcastMessage([
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
]);
}
监听通知
通知将通过私有频道广播,频道格式使用 {notifiable}.{id}
约定。因此,如果您正在向 App\Models\User
实例发送通知,并且该实例的 ID 为 1,则通知将通过 App.Models.User.1
私有频道进行广播。在使用 【Laravel Echo】 时,您可以轻松地在频道上监听通知,方法是使用 notification
方法:
Echo.private('App.Models.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
自定义通知频道
如果您希望自定义实体的广播通知所在的频道,您可以在可通知实体中定义一个 receivesBroadcastNotificationsOn
方法:
<?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;
/**
* 用户接收广播通知的频道。
*/
public function receivesBroadcastNotificationsOn(): string
{
return 'users.'.$this->id;
}
}
短信通知
前提条件
在 Laravel 中发送 SMS 通知是通过 Vonage(前身为 Nexmo)实现的。在您能够通过 Vonage 发送通知之前,您需要安装 laravel/vonage-notification-channel
和 guzzlehttp/guzzle
包:
composer require laravel/vonage-notification-channel guzzlehttp/guzzle
该包包含了一个【配置文件】,但您并不需要将此配置文件导出到您的应用程序中。您可以直接使用 VONAGE_KEY
和 VONAGE_SECRET
环境变量来定义您的 Vonage 公共密钥和私密密钥。
在定义好密钥后,您还应设置一个 VONAGE_SMS_FROM
环境变量,指定默认的 SMS 消息发送号码。您可以在 Vonage 控制面板中生成此号码:
VONAGE_SMS_FROM=15556666666
格式化短信通知
如果通知支持以 SMS 形式发送,您应该在通知类中定义一个 toVonage
方法。该方法将接收一个 $notifiable
实体,并应返回一个 Illuminate\Notifications\Messages\VonageMessage
实例:
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage / SMS 表示形式。
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('您的 SMS 消息内容');
}
Unicode 内容
如果您的 SMS 消息包含 Unicode 字符,您应该在构造 VonageMessage
实例时调用 unicode
方法:
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage / SMS 表示形式。
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('Your unicode message')
->unicode();
}
自定义 "From" 号码
如果您希望从与 VONAGE_SMS_FROM
环境变量中指定的电话号码不同的号码发送某些通知,您可以在 VonageMessage
实例上调用 from
方法:
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage / SMS 表示形式。
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->content('您的 SMS 消息内容')
->from('15554443333');
}
添加客户参考
如果您希望跟踪每个用户、团队或客户的费用,您可以在通知中添加 “客户参考”。Vonage 允许您使用此客户参考生成报告,以便更好地了解特定客户的 SMS 使用情况。客户参考可以是最长 40 个字符的任意字符串:
use Illuminate\Notifications\Messages\VonageMessage;
/**
* 获取通知的 Vonage / SMS 表示形式。
*/
public function toVonage(object $notifiable): VonageMessage
{
return (new VonageMessage)
->clientReference((string) $notifiable->id)
->content('您的 SMS 消息内容');
}
路由短信通知
为了将 Vonage 通知路由到正确的电话号码,在您的可通知实体上定义一个 routeNotificationForVonage
方法。
<?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;
/**
* 为 Vonage 渠道路由通知。
*/
public function routeNotificationForVonage(Notification $notification): string
{
return $this->phone_number;
}
}
Slack 通知
前提条件
在发送 Slack 通知之前,您应该通过 Composer 安装 Slack 通知渠道:
composer require laravel/slack-notification-channel
此外,您必须为您的 Slack 工作区创建一个 【Slack 应用】。
如果您只需要将通知发送到与应用创建所在的同一个 Slack 工作区,则应确保您的应用具有 chat:write
、chat:write.public
和 chat:write.customize
权限。如果您希望作为您的 Slack 应用发送消息,则应确保您的应用还具有 chat:write:bot
权限。这些权限可以在 Slack 的“OAuth & Permissions”应用管理标签页中添加。
接下来,复制应用的 “Bot 用户 OAuth 令牌”,并将其放入应用程序的 services.php
配置文件中的 Slack 配置数组中。该令牌可以在 Slack 的 “OAuth & Permissions” 标签页中找到:
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
],
应用分发
如果您的应用将向您的应用用户拥有的外部 Slack 工作区发送通知,您需要通过 Slack “分发” 您的应用。应用分发可以通过 Slack 中应用的 “Manage Distribution” 标签页进行管理。应用分发后,您可以使用 Socialite 代表您的应用用户【获取 Slack Bot 令牌】。
格式化 Slack 通知
如果一个通知支持作为 Slack 消息发送,您应该在通知类中定义一个 toSlack
方法。该方法将接收一个 $notifiable
实体,并应返回一个 Illuminate\Notifications\Slack\SlackMessage
实例。您可以使用 Slack 的 【Block Kit API】 构建丰富的通知。以下示例可以在 【Slack 的 Block Kit builder】 中预览:
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;
/**
* 获取通知的 Slack 表现形式。
*/
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text('您的发票已支付!')
->headerBlock('发票已支付')
->contextBlock(function (ContextBlock $block) {
$block->text('客户 #1234');
})
->sectionBlock(function (SectionBlock $block) {
$block->text('发票已支付。');
$block->field("*发票编号:*\n1000")->markdown();
$block->field("*发票接收者:*\ntaylor@laravel.com")->markdown();
})
->dividerBlock()
->sectionBlock(function (SectionBlock $block) {
$block->text('恭喜!');
});
}
使用 Slack 的 Block Kit Builder 模板
您可以提供由 Slack 的 Block Kit Builder 生成的原始 JSON 有效负载,而不是使用流式消息构建方法来构建 Block Kit 消息,方法是使用 usingBlockKitTemplate
:
use Illuminate\Notifications\Slack\SlackMessage;
use Illuminate\Support\Str;
/**
* 获取通知的 Slack 表现形式。
*/
public function toSlack(object $notifiable): SlackMessage
{
$template = <<<JSON
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "团队公告"
}
},
{
"type": "section",
"text": {
"type": "plain_text",
"text": "我们正在招聘!"
}
}
]
}
JSON;
return (new SlackMessage)
->usingBlockKitTemplate($template);
}
Slack 互动性
Slack 的 Block Kit 通知系统提供了强大的功能来处理用户交互。为了利用这些功能,您的 Slack 应用应该启用 “交互性” 并配置一个 “请求 URL”,该 URL 指向您的应用提供的一个 URL。这些设置可以在 Slack 中的 “交互性与快捷方式”应用管理标签页中进行管理。
在以下示例中,使用了 actionsBlock
方法,Slack 会向您的 “请求 URL” 发送一个 POST 请求,负载中包含点击按钮的 Slack 用户、被点击按钮的 ID 等信息。然后,您的应用可以根据负载中的信息决定要采取的操作。您还应该验证请求是否来自 Slack:
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;
/**
* 获取通知的 Slack 表现形式。
*/
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text('您的发票已支付!')
->headerBlock('发票已支付')
->contextBlock(function (ContextBlock $block) {
$block->text('客户 #1234');
})
->sectionBlock(function (SectionBlock $block) {
$block->text('发票已支付。');
})
->actionsBlock(function (ActionsBlock $block) {
// ID 默认为 "button_acknowledge_invoice"...
$block->button('确认发票')->primary();
// 手动配置 ID...
$block->button('拒绝')->danger()->id('deny_invoice');
});
}
确认模态框
如果您希望用户在执行操作之前必须确认该操作,可以在定义按钮时调用 confirm
方法。confirm
方法接受一条消息和一个闭包,闭包接收一个 ConfirmObject
实例:
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;
/**
* 获取通知的 Slack 表现形式。
*/
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text('您的发票已支付!')
->headerBlock('发票已支付')
->contextBlock(function (ContextBlock $block) {
$block->text('客户 #1234');
})
->sectionBlock(function (SectionBlock $block) {
$block->text('发票已支付。');
})
->actionsBlock(function (ActionsBlock $block) {
$block->button('确认发票')
->primary()
->confirm(
'确认支付并发送感谢邮件吗?',
function (ConfirmObject $dialog) {
$dialog->confirm('是');
$dialog->deny('否');
}
);
});
}
路由 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 用户 OAuth 令牌关联的工作区中的 #support-channel
频道:
<?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;
/**
* 为 Slack 渠道路由通知。
*/
public function routeNotificationForSlack(Notification $notification): mixed
{
return '#support-channel';
}
}
通知外部 Slack 工作空间
在向外部 Slack 工作区发送通知之前,您的 Slack 应用必须先进行【分发】。 |
当然,您通常会希望将通知发送到由您的应用用户拥有的 Slack 工作区。为了做到这一点,您首先需要获取该用户的 Slack OAuth 令牌。幸运的是,【Laravel Socialite】 包含一个 Slack 驱动程序,可以让您轻松地通过 Slack 进行应用用户身份验证并【获取 bot 令牌】。
一旦您获得了 bot 令牌并将其存储在应用程序的数据库中,您可以使用 SlackRoute::make
方法将通知路由到用户的工作区。此外,您的应用程序可能还需要为用户提供一个机会,让用户指定通知应该发送到哪个频道:
<?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;
/**
* 为 Slack 渠道路由通知。
*/
public function routeNotificationForSlack(Notification $notification): mixed
{
return SlackRoute::make($this->slack_channel, $this->slack_token);
}
}
本地化通知
Laravel 允许您以与 HTTP 请求当前语言环境不同的语言发送通知,甚至在通知排队时会记住这个语言环境。
为了实现这一点,Illuminate\Notifications\Notification
类提供了一个 locale
方法来设置所需的语言。应用程序会在评估通知时切换到这个语言环境,评估完成后会恢复到之前的语言环境:
$user->notify((new InvoicePaid($invoice))->locale('es'));
您还可以通过 Notification
facade 实现对多个可通知实体的本地化:
Notification::locale('es')->send(
$users, new InvoicePaid($invoice)
);
用户首选语言环境
有时,应用程序会存储每个用户的首选语言环境。通过在您的可通知模型中实现 HasLocalePreference
合约,您可以指示 Laravel 在发送通知时使用存储的语言环境:
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* 获取用户的首选语言环境。
*/
public function preferredLocale(): string
{
return $this->locale;
}
}
一旦实现了该接口,Laravel 会在向模型发送通知和邮件时自动使用首选语言环境。因此,使用此接口时无需调用 locale
方法:
$user->notify(new InvoicePaid($invoice));
测试
您可以使用 Notification
facade 的 fake
方法来防止通知的发送。通常,发送通知与您实际测试的代码无关。大多数情况下,只需断言 Laravel 是否被指示发送了给定的通知就足够了。
在调用 Notification
facade 的 fake
方法之后,您可以断言通知是否已指示发送给用户,并检查通知接收到的数据:
<?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 that a given number of notifications were sent...
Notification::assertCount(3);
});
<?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 that a given number of notifications were sent...
Notification::assertCount(3);
}
}
您可以将一个闭包传递给 assertSentTo
或 assertNotSentTo
方法,以断言发送的通知通过给定的 “真值测试”。如果至少有一个通知通过了给定的真值测试,则断言会成功:
Notification::assertSentTo(
$user,
function (OrderShipped $notification, array $channels) use ($order) {
return $notification->order->id === $order->id;
}
);
按需通知
如果您正在测试的代码发送了【按需通知】,您可以通过 assertSentOnDemand
方法来测试该按需通知是否已发送:
Notification::assertSentOnDemand(OrderShipped::class);
通过将一个闭包作为第二个参数传递给 assertSentOnDemand
方法,您可以判断按需通知是否已发送到正确的“路由”地址:
Notification::assertSentOnDemand(
OrderShipped::class,
function (OrderShipped $notification, array $channels, object $notifiable) use ($user) {
return $notifiable->routes['mail'] === $user->email;
}
);
通知事件
通知发送事件
当通知正在发送时,通知系统会触发 Illuminate\Notifications\Events\NotificationSending
事件。该事件包含了 “可通知实体” 和通知实例本身。您可以在应用程序中为此事件创建【事件监听器】:
use Illuminate\Notifications\Events\NotificationSending;
class CheckNotificationStatus
{
/**
* 处理给定的事件。
*/
public function handle(NotificationSending $event): void
{
// ...
}
}
如果 NotificationSending
事件的事件监听器的 handle
方法返回 false
,则通知将不会被发送:
/**
* 处理给定的事件。
*/
public function handle(NotificationSending $event): bool
{
return false;
}
在事件监听器中,您可以访问事件中的 notifiable
、notification
和 channel
属性,以了解更多关于通知接收者或通知本身的信息:
/**
* 处理给定的事件。
*/
public function handle(NotificationSending $event): void
{
// $event->channel
// $event->notifiable
// $event->notification
}
通知已发送事件
当通知已发送时,通知系统会触发 Illuminate\Notifications\Events\NotificationSent
事件。该事件包含了 “可通知实体” 和通知实例本身。您可以在应用程序中为此事件创建【事件监听器】:
use Illuminate\Notifications\Events\NotificationSent;
class LogNotification
{
/**
* 处理给定的事件。
*/
public function handle(NotificationSent $event): void
{
// ...
}
}
在事件监听器中,您可以访问事件中的 notifiable
、notification
、channel
和 response
属性,以了解更多关于通知接收者或通知本身的信息:
/**
* 处理给定的事件。
*/
public function handle(NotificationSent $event): void
{
// $event->channel
// $event->notifiable
// $event->notification
// $event->response
}
自定义通道
Laravel 提供了一些内置的通知渠道,但您可能希望编写自己的驱动程序,通过其它渠道发送通知。Laravel 使这变得非常简单。要开始使用,只需定义一个包含 send
方法的类。该方法应该接收两个参数:$notifiable
和 $notification
。
在 send
方法中,您可以调用通知上的方法来获取该渠道可以理解的消息对象,然后以您希望的方式将通知发送给 $notifiable
实例:
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class VoiceChannel
{
/**
* 发送给定的通知。
*/
public function send(object $notifiable, Notification $notification): void
{
$message = $notification->toVoice($notifiable);
// 将通知发送到 $notifiable 实例...
}
}
一旦定义了您的通知渠道类,您可以在任何通知的 via
方法中返回该类的名称。在这个例子中,您的通知的 toVoice
方法可以返回您选择的任何对象来表示语音消息。例如,您可以定义自己的 VoiceMessage
类来表示这些消息:
<?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;
/**
* 获取通知的渠道。
*/
public function via(object $notifiable): string
{
return VoiceChannel::class;
}
/**
* 获取通知的语音表示。
*/
public function toVoice(object $notifiable): VoiceMessage
{
// ...
}
}