Laravel通知系统
用户注册、订单状态变化、密码重置和系统告警等场景,都需要向用户发送提醒。Laravel Notifications 支持邮件、数据库、广播和短信等通知渠道,可以将通知内容统一管理,再根据业务选择发送方式。
下面以订单创建后的通知为例,介绍邮件通知和数据库通知的基本用法。
一、创建通知类
在项目根目录执行:
php artisan make:notification OrderCreatedNotification
Laravel 会生成:
app/Notifications/OrderCreatedNotification.php
通知类可以同时定义多个发送渠道,例如邮件和数据库。
二、定义通知渠道
编辑通知文件:
<?php
namespace App\Notifications;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class OrderCreatedNotification extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public Order $order
) {
}
public function via(object $notifiable): array
{
return ['mail', 'database'];
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('订单创建成功')
->greeting('您好')
->line("您的订单 {$this->order->id} 已创建成功。")
->line("订单金额:{$this->order->amount}")
->action('查看订单', url("/orders/{$this->order->id}"))
->line('感谢您的使用。');
}
public function toDatabase(object $notifiable): array
{
return [
'order_id' => $this->order->id,
'message' => "订单 {$this->order->id} 已创建成功",
'status' => $this->order->status,
];
}
}
via() 决定通知使用哪些渠道:
mail:发送邮件。database:保存到数据库。broadcast:通过广播推送。vonage:使用 Vonage 短信服务。
本示例实现了邮件和数据库两种渠道。
三、让用户支持通知
Laravel 默认会在 User 模型中引入 Notifiable Trait:
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
}
如果项目中没有该 Trait,需要手动添加。
邮件通知默认使用用户模型中的 email 字段。也可以通过 routeNotificationForMail() 自定义邮件地址:
public function routeNotificationForMail(
$notification
): string {
return $this->notification_email;
}
自定义字段必须存在于用户模型或相关业务数据中。
四、创建通知数据表
数据库通知需要使用 Laravel 的通知表。执行:
php artisan notifications:table
php artisan migrate
迁移完成后,通知会保存到 notifications 表。
表中通常包含:
- 通知 ID。
- 接收用户类型和 ID。
- 通知类型。
- 通知数据。
- 已读时间。
- 创建和更新时间。
执行迁移前,应确认项目数据库连接配置正确。
五、发送通知
假设当前用户已经登录,订单创建成功后可以发送通知:
use App\Notifications\OrderCreatedNotification;
$user->notify(
new OrderCreatedNotification($order)
);
也可以通过通知门面发送:
use Illuminate\Support\Facades\Notification;
use App\Notifications\OrderCreatedNotification;
Notification::send(
$users,
new OrderCreatedNotification($order)
);
其中,$users 可以是用户集合,也可以是实现了通知接收能力的对象集合。
六、配置邮件服务
邮件通知需要先配置邮件服务。.env 示例:
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-account@example.com
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="${APP_NAME}"
具体参数应以邮件服务商提供的配置为准。
修改配置后,如果项目启用了配置缓存,需要执行:
php artisan config:clear
php artisan config:cache
开发环境也可以使用日志邮件驱动:
MAIL_MAILER=log
使用 log 驱动时,邮件内容会写入应用日志,适合检查模板和发送流程,不会真正发送到收件箱。
七、使用队列异步发送通知
邮件发送可能增加接口响应时间,因此示例中的通知实现了 ShouldQueue:
class OrderCreatedNotification extends Notification implements ShouldQueue
{
use Queueable;
}
项目需要配置可用的异步队列,例如:
QUEUE_CONNECTION=database
如果使用数据库队列,需要创建任务表:
php artisan make:queue-table
php artisan migrate
然后启动队列工作进程:
php artisan queue:work
订单创建后,通知会进入队列,由工作进程负责发送。
生产环境应使用 Supervisor、Horizon 或其他进程管理工具保持队列工作进程运行。邮件发送失败时,还应根据业务设置重试次数和失败任务处理方式。
八、读取未读站内通知
在控制器中读取当前用户的未读通知:
public function index()
{
$notifications = request()
->user()
->unreadNotifications()
->latest()
->paginate(20);
return response()->json($notifications);
}
在 Blade 模板中显示:
@foreach (auth()->user()->unreadNotifications as $notification)
<div>
{{ $notification->data['message'] ?? '你有一条新通知' }}
</div>
@endforeach
通知数据来自 toDatabase() 返回的数组。读取不存在的键时,可以使用 ?? 设置默认内容。
九、标记通知为已读
标记一条通知为已读:
$notification = auth()
->user()
->notifications()
->findOrFail($notificationId);
$notification->markAsRead();
全部标记为已读:
auth()
->user()
->unreadNotifications
->markAsRead();
也可以直接更新未读通知:
auth()
->user()
->unreadNotifications()
->update([
'read_at' => now(),
]);
操作通知时,应确保查询范围属于当前登录用户,避免通过修改通知 ID 访问其他用户的通知。
十、删除通知
删除单条通知:
auth()
->user()
->notifications()
->whereKey($notificationId)
->delete();
删除当前用户的全部通知:
auth()
->user()
->notifications()
->delete();
通知删除属于不可逆操作,后台系统可以先提供“标记为已读”功能,再根据保留策略定期清理旧通知。
十一、使用数据库通知模板
如果不希望直接在 Blade 中读取数组,可以在通知中提供更明确的字段:
public function toDatabase(object $notifiable): array
{
return [
'type' => 'order_created',
'title' => '订单创建成功',
'message' => "订单 {$this->order->id} 已创建成功",
'url' => url("/orders/{$this->order->id}"),
];
}
模板中调用:
@foreach (auth()->user()->unreadNotifications as $notification)
<a href="{{ $notification->data['url'] }}">
<strong>{{ $notification->data['title'] }}</strong>
<span>{{ $notification->data['message'] }}</span>
</a>
@endforeach
通知数据结构确定后,应避免随意修改字段名称,否则前端模板可能无法正常展示。
十二、测试通知发送
Laravel 提供 Notification::fake() 模拟通知:
use Illuminate\Support\Facades\Notification;
Notification::fake();
$user->notify(
new OrderCreatedNotification($order)
);
Notification::assertSentTo(
$user,
OrderCreatedNotification::class
);
如果需要确认通知使用了正确渠道:
Notification::assertSentTo(
$user,
OrderCreatedNotification::class,
function ($notification, $channels) {
return in_array('mail', $channels, true)
&& in_array('database', $channels, true);
}
);
测试通知时,使用模拟方法可以避免实际发送邮件或写入真实通知数据。
十三、常见问题
通知没有发送邮件怎么办?
先检查 .env 中的邮件配置,再执行:
php artisan config:clear
如果通知实现了 ShouldQueue,还要确认队列工作进程正在运行:
php artisan queue:work
同时检查失败任务:
php artisan queue:failed
为什么数据库中没有通知记录?
确认通知的 via() 方法包含 database,并且已经执行:
php artisan notifications:table
php artisan migrate
如果通知使用队列,还需要等待队列工作进程处理任务。
邮件和站内通知可以使用不同内容吗?
可以。分别在 toMail() 和 toDatabase() 中定义内容即可。邮件适合较完整的说明,站内通知可以只保留标题、摘要和跳转地址。
通知中的订单信息被重复处理怎么办?
队列通知可能因为失败重试而执行多次。涉及积分、库存或外部扣款时,应为业务操作设计幂等机制,避免重复产生结果。
广告合作
QQ群号:4114653
- 本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。邮箱:2942802716#qq.com(#改为@)。
- 本站原创内容未经允许不得转载,转载请注明出处“站长百科”和原文地址。