Laravel 广播与实时通知:用事件 + Redis 通道把订单状态推到前端
test12026-09-241 次阅读
为什么需要服务端主动推送
订单状态变化(已支付、已发货、已签收)如果让前端轮询接口,既浪费连接也做不到实时。Laravel 的广播机制把"事件"通过驱动(常用 Redis + WebSocket 网关)推到前端订阅的频道,浏览器用 Laravel Echo 监听即可,无需自己写长轮询。
定义可广播的事件
事件类实现 ShouldBroadcast 接口,并指定 broadcastOn 返回的频道。Laravel 会在事件被触发(event(...) 或 dispatch)时,把事件载荷序列化后发往配置的广播驱动。
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
class OrderStatusChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets;
public function __construct(
public int $orderId,
public string $status,
public string $userId
) {}
public function broadcastOn(): array
{
// 每个用户一个私有频道,只有本人能订阅
return [new Channel("orders.{$this->userId}")];
}
public function broadcastWith(): array
{
return [
"order_id" => $this->orderId,
"status" => $this->status,
"at" => now()->toIso8601String(),
];
}
}
触发事件
在业务代码里触发即可,其余交给框架。注意私有频道需要前端鉴权,Laravel 自带广播鉴权路由,用 Sanctum/Auth 守卫判断用户是否有权订阅该频道。
<?php
use App\Events\OrderStatusChanged;
// 支付回调里
public function paid(int $orderId, int $userId)
{
// ...更新订单状态到数据库
event(new OrderStatusChanged($orderId, "paid", (string) $userId));
}
前端用 Echo 监听
前端引入 laravel-echo 与 socket.io-client,连接 WebSocket 网关(如 Soketi / Pusher),订阅私有频道并监听事件名(默认是事件类短名)。
import Echo from "laravel-echo";
import io from "socket.io-client";
const echo = new Echo({
broadcaster: "socket.io",
host: "https://ws.example.com",
auth: { headers: { Authorization: `Bearer ${token}` } },
});
echo.private(`orders.${userId}`)
.listen("OrderStatusChanged", (e) => {
console.log("订单状态更新", e.status, e.order_id);
// 更新界面提示,无需轮询
});
配置与部署要点
- 服务端 .env 设 BROADCAST_DRIVER=redis,并启动一个 WebSocket 网关(Soketi/Pusher 兼容)和队列 worker 处理广播任务。
- 私有频道需后端暴露 /broadcasting/auth 路由做订阅鉴权,未授权订阅会被拒绝。
- 生产环境给 WebSocket 网关挂上 TLS 与连接数限制,频道按用户维度隔离,避免越权收到他人事件。
T
test1
文章作者
为什么需要服务端主动推送 订单状态变化(已支付、已发货、已签收)如果让前端轮询接口,既浪费连接也做不到实时。Larav...
- 分类
- 技术
- 发布时间
- 2026-09-24
- 字数
- 约 2041 字
- 阅读
- 1 次
