Laravel通知系统使用指南:用户通知与消息推送功能实现
在开发Web应用时,用户通知和消息推送是提升用户体验的核心功能。Laravel内置的通知系统提供了统一的接口,支持多种通知渠道,包括邮件、短信、数据库存储、Slack等,开发者可以快速实现各类通知场景。本文将详细介绍Laravel通知系统的使用方法,以及如何实现用户通知和消息推送功能。
一、通知系统核心概念
Laravel的通知系统围绕两个核心概念展开:通知类和可通知实体。通知类定义了通知的内容和发送逻辑,可通知实体则是接收通知的对象,通常是用户模型,只要模型使用了Illuminate\Notifications\Notifiable trait就具备接收通知的能力。
二、创建通知类
首先我们需要通过Artisan命令创建通知类,假设我们要实现一个订单发货通知:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class OrderShipped extends Notification implements ShouldQueue
{
use Queueable;
// 订单信息
protected $order;
/**
* 初始化通知,传入订单数据
*
* @param $order 订单实例
*/
public function __construct($order)
{
$this->order = $order;
}
/**
* 定义通知发送的渠道
* 支持mail、database、sms、slack等,也可以自定义渠道
*
* @param mixed $notifiable 可通知实体
* @return array
*/
public function via($notifiable)
{
// 同时发送到邮件和数据库存储,方便用户查看历史通知
return ['mail', 'database'];
}
/**
* 邮件渠道的通知内容
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('您的订单已发货')
->greeting('您好,' . $notifiable->name)
->line('您的订单 #' . $this->order->id . ' 已发货,预计3天内送达。')
->action('查看订单详情', url('/orders/' . $this->order->id))
->line('感谢您使用我们的服务!');
}
/**
* 数据库渠道的通知内容
* 返回数组会直接存储到notifications表的data字段
*
* @param mixed $notifiable
* @return array
*/
public function toDatabase($notifiable)
{
return [
'order_id' => $this->order->id,
'total_amount' => $this->order->total_amount,
'shipped_at' => now()->toDateTimeString(),
'message' => '您的订单 #' . $this->order->id . ' 已发货'
];
}
/**
* 获取通知的唯一标识
*
* @return string
*/
public function broadcastType()
{
return 'order.shipped';
}
}上面的通知类实现了ShouldQueue接口,代表通知会走队列异步发送,避免阻塞主流程。如果不需要队列,可以去掉该接口和Queueable trait。
三、配置可通知的用户模型
Laravel默认的用户模型已经引入了Notifiable trait,如果没有的话可以手动添加:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
// 其他模型属性和方法
}Notifiable trait提供了notify方法,用于给当前用户发送通知,还提供了notifications关系方法,方便查询用户的通知列表。
四、发送通知
发送通知非常简单,只需要获取到可通知的实体(比如用户实例),然后调用notify方法传入通知类实例即可:
<?php
namespace App\Http\Controllers;
use App\Models\Order;
use App\Models\User;
use App\Notifications\OrderShipped;
use Illuminate\Http\Request;
class OrderController extends Controller
{
/**
* 处理订单发货逻辑
*
* @param Request $request
* @param $orderId 订单ID
*/
public function shipOrder(Request $request, $orderId)
{
$order = Order::findOrFail($orderId);
// 假设订单关联了用户,获取下单用户
$user = $order->user;
// 给用户发送发货通知
$user->notify(new OrderShipped($order));
return response()->json(['message' => '订单发货通知已发送']);
}
}如果需要给多个用户发送通知,可以使用Notification门面:
use Illuminate\Support\Facades\Notification;
// 给所有管理员用户发送通知
$admins = User::where('is_admin', 1)->get();
Notification::send($admins, new OrderShipped($order));五、数据库通知的存储与查询
使用数据库渠道存储通知前,需要先执行通知表迁移命令:
php artisan notifications:table php artisan migrate
迁移完成后会生成notifications表,存储所有数据库渠道的通知记录。我们可以通过用户模型的notifications关系查询通知:
// 获取用户所有未读通知
$unreadNotifications = auth()->user()->unreadNotifications;
// 获取用户所有已读通知
$readNotifications = auth()->user()->readNotifications;
// 获取用户所有通知,按创建时间倒序
$allNotifications = auth()->user()->notifications()->orderBy('created_at', 'desc')->paginate(10);
// 标记单个通知为已读
$notification = auth()->user()->unreadNotifications()->find($notificationId);
if ($notification) {
$notification->markAsRead();
}
// 标记所有通知为已读
auth()->user()->unreadNotifications->markAsRead();六、实现实时消息推送
如果需要在用户收到通知时实时推送提醒,可以结合Laravel的广播功能实现。首先需要配置广播驱动,以Pusher为例,修改config/broadcasting.php配置:
// config/broadcasting.php
return [
'default' => env('BROADCAST_DRIVER', 'pusher'),
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => true,
'host' => env('PUSHER_HOST', 'api-mt1.pusher.com'),
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https'),
],
],
// 其他广播驱动配置
],
];然后在通知类中添加toBroadcast方法,定义广播的内容:
// 在OrderShipped通知类中添加
/**
* 广播渠道的通知内容
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\BroadcastMessage
*/
public function toBroadcast($notifiable)
{
return new \Illuminate\Notifications\Messages\BroadcastMessage([
'order_id' => $this->order->id,
'message' => '您的订单 #' . $this->order->id . ' 已发货,请注意查收',
'created_at' => now()->toDateTimeString()
]);
}同时需要在via方法中添加broadcast渠道:
public function via($notifiable)
{
return ['mail', 'database', 'broadcast'];
}前端可以监听对应的广播频道,收到通知后实时展示提醒,比如使用Laravel Echo配合Pusher:
// 假设用户ID是1,私有频道格式是private-App.Models.User.1
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: 'your-pusher-key',
cluster: 'your-pusher-cluster',
forceTLS: true
});
// 监听私有频道通知
window.Echo.private(`App.Models.User.${userId}`)
.notification((notification) => {
console.log('收到新通知:', notification);
// 这里可以调用前端的提示组件展示通知
alert(notification.message);
});七、自定义通知渠道
如果官方提供的渠道不能满足需求,我们可以自定义通知渠道。比如实现一个企业微信通知渠道,首先创建渠道类:
<?php
namespace App\Notifications\Channels;
use Illuminate\Notifications\Notification;
class WeChatWorkChannel
{
/**
* 发送通知到企业微信
*
* @param mixed $notifiable 可通知实体
* @param Notification $notification 通知实例
*/
public function send($notifiable, Notification $notification)
{
// 获取通知内容
$message = $notification->toWeChatWork($notifiable);
// 这里编写调用企业微信API发送消息的逻辑
// 比如使用Guzzle发送HTTP请求到企业微信机器人接口
// $response = Http::post('企业微信webhook地址', $message);
}
}然后在通知类中定义toWeChatWork方法,并把wechat_work添加到via方法的返回数组中即可:
// 在通知类中添加
public function via($notifiable)
{
return ['mail', 'database', 'wechat_work'];
}
public function toWeChatWork($notifiable)
{
return [
'msgtype' => 'text',
'text' => [
'content' => '您的订单已发货,订单号:' . $this->order->id
]
];
}八、注意事项
- 如果通知需要异步发送,确保队列服务已经启动,执行
php artisan queue:work监听队列任务。 - 广播功能需要正确配置广播驱动和前端Echo,同时私有频道需要用户已认证,保证通知的安全性。
- 数据库通知的
data字段是JSON格式,查询时可以使用whereJsonContains等方法筛选特定内容的通知。 - 生产环境中建议将敏感配置(如Pusher密钥、邮件配置等)放到
.env文件中,不要硬编码到代码里。