什么是 Notification
Laravel 的通知(Notification)系统提供了统一的 API,可以向邮件、SMS、Slack、数据库等多个渠道发送通知。
Mail 与 Notification 的区别
| 比较 | Mail | Notification |
|---|
| 主要用途 | 富文本 HTML 邮件 | 简短的信息通知 |
| 支持渠道 | 仅邮件 | 邮件、DB、Slack、SMS 等多种 |
| 模板 | 完全自由 | 简单的消息格式 |
像“发票已支付”这类需要向多个渠道发送同一条通知的场景,Notification 更合适。
创建 Notification 类
使用 make:notification Artisan 命令生成通知类。
php artisan make:notification InvoicePaid
生成的类位于 app/Notifications/。类中包含 via() 方法以及为各个渠道生成消息的方法。
发送通知的方式
使用 Notifiable trait
App\Models\User 默认已经包含了 Notifiable trait,可以用 notify() 方法发送通知。
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
Notifiable trait 并不限于 User 模型,任何模型都可以添加它。
使用 Notification Facade
要同时向多个用户发送,可以使用 Notification Facade。
use Illuminate\Support\Facades\Notification;
Notification::send($users, new InvoicePaid($invoice));
要立即发送(跳过队列),可以使用 sendNow()。
Notification::sendNow($developers, new DeploymentCompleted($deployment));
指定发送渠道
via() 方法以数组形式返回要使用的渠道。
public function via(object $notifiable): array
{
return ['mail', 'database'];
}
也可以根据用户偏好切换渠道。
public function via(object $notifiable): array
{
return $notifiable->prefers_sms ? ['vonage'] : ['mail', 'database'];
}
主要可用渠道如下。
| 渠道 | 键名 | 说明 |
|---|
| 邮件 | mail | 通过邮件发送通知 |
| 数据库 | database | 保存到数据库,在 UI 中展示 |
| 广播 | broadcast | 实时通知(WebSocket) |
| SMS | vonage | 通过 Vonage(原 Nexmo)发送短信 |
| Slack | slack | 发送到 Slack 频道 |
邮件渠道通知
toMail() 方法返回一个 MailMessage 实例。
use Illuminate\Notifications\Messages\MailMessage;
public function toMail(object $notifiable): MailMessage
{
$url = url('/invoice/'.$this->invoice->id);
return (new MailMessage)
->greeting('你好!')
->line('我们已收到你的发票付款。')
->action('查看发票', $url)
->line('感谢你的使用。');
}
MailMessage 的常用方法如下:
| 方法 | 说明 |
|---|
greeting() | 开头问候语 |
line() | 一行正文 |
action() | 按钮链接 |
subject() | 主题 |
from() | 发件人地址 |
mailer() | 使用的 mailer |
如果是错误通知,可以加上 error(),按钮会变红。
return (new MailMessage)
->error()
->subject('支付失败')
->line('发票支付处理失败。');
Markdown 通知邮件
也可以使用 Markdown 编写更丰富的邮件。加上 --markdown 选项生成类。
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
使用 toMarkdownMail() 代替 toMail()。
use Illuminate\Notifications\Messages\MailMessage;
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->markdown('mail.invoice.paid', [
'url' => url('/invoice/'.$this->invoice->id),
'invoice' => $this->invoice,
]);
}
数据库渠道通知
数据库渠道可以把通知保存到数据库,并在应用 UI 中展示。
准备表结构
先创建 notifications 表。
php artisan make:notifications-table
php artisan migrate
定义 toArray 方法
toArray() 方法以数组形式返回要保存的数据。
public function toArray(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
'message' => '我们已收到你的发票付款。',
];
}
这些数据会以 JSON 存放在 notifications 表的 data 列中。
获取通知
Notifiable trait 提供 notifications 关联,可用于读取通知。
$user = App\Models\User::find(1);
foreach ($user->notifications as $notification) {
echo $notification->type;
echo $notification->data['message'];
}
要只获取未读通知,使用 unreadNotifications。
foreach ($user->unreadNotifications as $notification) {
echo $notification->data['message'];
}
标记为已读
使用 markAsRead() 将通知标为已读。
// 逐条标记
foreach ($user->unreadNotifications as $notification) {
$notification->markAsRead();
}
// 一次性全部标记
$user->unreadNotifications->markAsRead();
// 使用查询批量更新
$user->unreadNotifications()->update(['read_at' => now()]);
通知的队列化
如果通知发送耗时较长,可以实现 ShouldQueue 接口并使用 Queueable trait,将通知放到队列中。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 后,调用 notify() 时通知会自动进入队列。
$user->notify(new InvoicePaid($invoice));
也支持延迟发送。
$user->notify(
(new InvoicePaid($invoice))->delay(now()->plus(minutes: 10))
);
同时向多个渠道发送
只要在 via() 中返回多个渠道并各自定义方法,就可以同时向多个渠道发送同一条通知。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
private readonly Invoice $invoice,
) {}
public function via(object $notifiable): array
{
// 同时发送到邮件和数据库
return ['mail', 'database'];
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('我们已收到你的发票付款')
->line('已确认收到发票 #'.$this->invoice->id.' 的付款。')
->action('查看发票', url('/invoice/'.$this->invoice->id));
}
public function toArray(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
}
按需通知(On-demand notifications)
要向没有账号的人发送通知,可以使用 Notification::route()。
use Illuminate\Support\Facades\Notification;
Notification::route('mail', '[email protected]')
->route('vonage', '5555551212')
->notify(new InvoicePaid($invoice));
| 想做的事 | 方法 |
|---|
| 创建 Notification | php artisan make:notification ClassName |
| 向用户发送通知 | $user->notify(new MyNotification()) |
| 向多个用户发送 | Notification::send($users, new MyNotification()) |
| 使用邮件发送 | 在 via() 中返回 mail 并定义 toMail() |
| 保存到数据库 | 在 via() 中返回 database 并定义 toArray() |
| 使用队列异步发送 | 实现 ShouldQueue |
| 发送到多个渠道 | 在 via() 中返回多个键 |