本文目录导读:

我将为您创建一个完整的Laravel广播事件驱动实时更新系统示例。
项目设置
安装必要的包
# 安装Laravel Reverb(Laravel 11+) composer require laravel/reverb # 或使用Pusher composer require pusher/pusher-php-server # 前端依赖 npm install --save laravel-echo pusher-js
配置.env文件
# Reverb配置 BROADCAST_CONNECTION=reverb REVERB_APP_ID=local REVERB_APP_KEY=local REVERB_APP_SECRET=secret REVERB_HOST="127.0.0.1" REVERB_PORT=8080 REVERB_SCHEME=http # 或 Pusher配置 # BROADCAST_CONNECTION=pusher # PUSHER_APP_ID=your-app-id # PUSHER_APP_KEY=your-key # PUSHER_APP_SECRET=your-secret # PUSHER_APP_HOST=127.0.0.1 # PUSHER_APP_PORT=443 # PUSHER_APP_SCHEME=https
创建广播事件
创建订单事件
<?php
// app/Events/OrderCreated.php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderCreated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $order;
/**
* Create a new event instance.
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new Channel('orders'),
];
}
/**
* 自定义广播数据
*/
public function broadcastWith(): array
{
return [
'order_id' => $this->order->id,
'customer_name' => $this->order->customer_name,
'total_amount' => $this->order->total_amount,
'status' => $this->order->status,
'created_at' => $this->order->created_at->toISOString(),
];
}
/**
* 自定义广播事件名称
*/
public function broadcastAs(): string
{
return 'order.created';
}
}
创建订单更新事件
<?php
// app/Events/OrderStatusUpdated.php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderStatusUpdated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $order;
public $oldStatus;
public $newStatus;
public function __construct(Order $order, $oldStatus, $newStatus)
{
$this->order = $order;
$this->oldStatus = $oldStatus;
$this->newStatus = $newStatus;
}
public function broadcastOn(): array
{
return [
new Channel('orders'),
new PrivateChannel('user.' . $this->order->user_id),
];
}
public function broadcastWith(): array
{
return [
'order_id' => $this->order->id,
'old_status' => $this->oldStatus,
'new_status' => $this->newStatus,
'updated_at' => now()->toISOString(),
];
}
public function broadcastAs(): string
{
return 'order.status.updated';
}
}
创建实时通知事件
<?php
// app/Events/NewNotification.php
namespace App\Events;
use App\Models\Notification;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NewNotification implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $notification;
public function __construct(Notification $notification)
{
$this->notification = $notification;
}
public function broadcastOn(): array
{
return [
new Channel('notifications.' . $this->notification->user_id),
];
}
public function broadcastWith(): array
{
return [
'id' => $this->notification->id,
'type' => $this->notification->type,
'message' => $this->notification->message,
'data' => $this->notification->data,
'read' => $this->notification->read_at !== null,
'created_at' => $this->notification->created_at->toISOString(),
];
}
public function broadcastAs(): string
{
return 'notification.created';
}
}
创建模型事件监听
Order模型
<?php
// app/Models/Order.php
namespace App\Models;
use App\Events\OrderCreated;
use App\Events\OrderStatusUpdated;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'customer_name',
'customer_email',
'total_amount',
'status',
'items'
];
protected $casts = [
'items' => 'array',
'total_amount' => 'decimal:2',
];
protected $dispatchesEvents = [
'created' => OrderCreated::class,
];
// 状态更新自动触发事件
public function updateStatus(string $newStatus)
{
$oldStatus = $this->status;
$this->update(['status' => $newStatus]);
event(new OrderStatusUpdated($this, $oldStatus, $newStatus));
return $this;
}
// 关联用户
public function user()
{
return $this->belongsTo(User::class);
}
}
创建控制器
<?php
// app/Http/Controllers/OrderController.php
namespace App\Http\Controllers;
use App\Events\OrderCreated;
use App\Events\OrderStatusUpdated;
use App\Models\Order;
use App\Models\Notification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class OrderController extends Controller
{
/**
* 创建订单并触发事件
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'customer_name' => 'required|string|max:255',
'customer_email' => 'required|email',
'total_amount' => 'required|numeric|min:0',
'items' => 'required|array',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$order = Order::create([
'user_id' => auth()->id(),
...$request->all()
]);
// 手动触发事件
event(new OrderCreated($order));
// 创建通知
$notification = Notification::create([
'user_id' => auth()->id(),
'type' => 'order_confirmation',
'message' => "Order #{$order->id} confirmed successfully",
'data' => [
'order_id' => $order->id,
'total' => $order->total_amount
]
]);
// 触发通知事件
event(new NewNotification($notification));
return response()->json([
'message' => 'Order created successfully',
'order' => $order
], 201);
}
/**
* 更新订单状态
*/
public function updateStatus(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'status' => 'required|in:pending,processing,shipped,delivered,cancelled'
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$order = Order::findOrFail($id);
$order->updateStatus($request->status);
// 创建状态更新通知
$notification = Notification::create([
'user_id' => $order->user_id,
'type' => 'order_status',
'message' => "Order #{$order->id} status changed to {$request->status}",
'data' => [
'order_id' => $order->id,
'status' => $request->status
]
]);
event(new NewNotification($notification));
return response()->json([
'message' => 'Order status updated successfully',
'order' => $order
]);
}
/**
* 标记通知为已读
*/
public function markNotificationRead($notificationId)
{
$notification = Notification::findOrFail($notificationId);
$notification->update(['read_at' => now()]);
return response()->json(['message' => 'Notification marked as read']);
}
}
前端JavaScript设置
创建resources/js/bootstrap.js配置
// resources/js/bootstrap.js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
// Reverb配置
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT ?? 8080,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
enabledTransports: ['ws', 'wss'],
});
// Pusher配置(如果使用Pusher)
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: import.meta.env.VITE_PUSHER_APP_KEY,
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
// forceTLS: true
// });
创建实时监听组件
// resources/js/live/realtime-handlers.js
export class RealtimeHandler {
constructor() {
this.orderChannel = null;
this.notificationChannels = [];
this.init();
}
init() {
// 监听所有订单事件
this.setupOrderListeners();
// 监听用户私有通知(需要认证)
this.setupNotificationListeners();
}
setupOrderListeners() {
this.orderChannel = window.Echo.channel('orders');
// 监听新订单
this.orderChannel.listen('.order.created', (data) => {
this.handleNewOrder(data);
});
// 监听订单状态更新
this.orderChannel.listen('.order.status.updated', (data) => {
this.handleOrderStatusUpdate(data);
});
}
setupNotificationListeners() {
// 获取当前用户ID
const userId = document.querySelector('meta[name="user-id"]')?.content;
if (userId) {
this.setupPrivateChannel(userId);
}
}
setupPrivateChannel(userId) {
console.log(`Setting up private channel for user: ${userId}`);
// 监听用户私有通知通道
const channel = window.Echo.channel(`user.${userId}`);
// 监听订单状态更新
channel.listen('.order.status.updated', (data) => {
this.handlePrivateOrderUpdate(data);
});
// 监听新通知
const notificationChannel = window.Echo.channel(`notifications.${userId}`);
notificationChannel.listen('.notification.created', (data) => {
this.handleNewNotification(data);
});
this.notificationChannels.push(channel, notificationChannel);
}
handleNewOrder(data) {
console.log('New order received:', data);
// 创建通知DOM元素
const notification = this.createNotificationElement(
'New Order',
`Order #${data.order_id} from ${data.customer_name} for $${data.total_amount}`,
'success'
);
this.appendNotification(notification);
// 播放声音提示
this.playNotificationSound();
// 更新订单计数
this.updateOrderCounter();
// 触发自定义事件
window.dispatchEvent(new CustomEvent('order:created', { detail: data }));
}
handleOrderStatusUpdate(data) {
console.log('Order status updated:', data);
// 更新UI
this.updateOrderStatusUI(data.order_id, data.new_status);
// 显示通知
const notification = this.createNotificationElement(
'Order Updated',
`Order #${data.order_id} has been ${data.new_status}`,
'info'
);
this.appendNotification(notification);
// 触发自定义事件
window.dispatchEvent(new CustomEvent('order:status-updated', { detail: data }));
}
handleNewNotification(data) {
console.log('New notification:', data);
const notification = this.createNotificationElement(
'Notification',
data.message,
'info'
);
this.appendNotification(notification);
this.updateNotificationBadge();
// 触发自定义事件
window.dispatchEvent(new CustomEvent('notification:received', { detail: data }));
}
createNotificationElement(title, message, type) {
const container = document.createElement('div');
container.className = `notification-item alert alert-${type} fade-in`;
container.innerHTML = `
<div class="notification-content">
<h4>${title}</h4>
<p>${message}</p>
</div>
<button class="close-btn" onclick="this.parentElement.remove()">×</button>
`;
return container;
}
appendNotification(notification) {
const notificationContainer = document.querySelector('#notification-container');
if (notificationContainer) {
notificationContainer.appendChild(notification);
// 自动移除通知
setTimeout(() => {
notification.classList.add('fade-out');
setTimeout(() => notification.remove(), 300);
}, 5000);
}
}
updateOrderStatusUI(orderId, status) {
const orderElement = document.querySelector(`[data-order-id="${orderId}"]`);
if (orderElement) {
const statusElement = orderElement.querySelector('.order-status');
if (statusElement) {
statusElement.textContent = status;
statusElement.className = `order-status badge-${status}`;
}
}
}
playNotificationSound() {
const audio = new Audio('/sounds/notification.mp3');
audio.play().catch(() => console.log('Sound playback blocked'));
}
updateOrderCounter() {
const counter = document.querySelector('#order-counter');
if (counter) {
const current = parseInt(counter.textContent) || 0;
counter.textContent = current + 1;
}
}
updateNotificationBadge() {
const badge = document.querySelector('#notification-badge');
if (badge) {
const current = parseInt(badge.dataset.count) || 0;
badge.dataset.count = current + 1;
badge.textContent = current + 1;
}
}
destroy() {
// 清理监听器
this.orderChannel?.stopListening('.order.created');
this.orderChannel?.stopListening('.order.status.updated');
this.notificationChannels.forEach(channel => {
channel.stopListening('.order.status.updated');
channel.stopListening('.notification.created');
});
}
}
// 初始化实时处理程序
export const realtimeHandler = new RealtimeHandler();
在应用入口引入
// resources/js/app.js import './bootstrap'; import './live/realtime-handlers';
Blade模板
{{-- resources/views/orders/index.blade.php --}}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="user-id" content="{{ auth()->id() }}">Live Orders</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
<div class="container">
<h1>Live Orders Dashboard</h1>
<!-- 通知区域 -->
<div id="notification-container"></div>
<!-- 统计信息 -->
<div class="stats-grid">
<div class="stat-card">
<h3>New Orders</h3>
<span id="order-counter">0</span>
</div>
<div class="stat-card">
<h3>Notifications</h3>
<span id="notification-badge" data-count="0">0</span>
</div>
</div>
<!-- 订单列表 -->
<div class="orders-list">
@foreach($orders as $order)
<div class="order-card" data-order-id="{{ $order->id }}">
<div class="order-header">
<h3>Order #{{ $order->id }}</h3>
<span class="order-status badge-{{ $order->status }}">{{ $order->status }}</span>
</div>
<div class="order-body">
<p>Customer: <strong>{{ $order->customer_name }}</strong></p>
<p>Total: <strong>${{ number_format($order->total_amount, 2) }}</strong></p>
<p>Created: <strong>{{ $order->created_at->format('Y-m-d H:i:s') }}</strong></p>
</div>
<div class="order-actions">
<select class="status-select" onchange="updateStatus({{ $order->id }}, this.value)">
<option value="">Select Status</option>
@foreach(['pending', 'processing', 'shipped', 'delivered', 'cancelled'] as $status)
<option value="{{ $status }}" {{ $order->status === $status ? 'selected' : '' }}>
{{ ucfirst($status) }}
</option>
@endforeach
</select>
</div>
</div>
@endforeach
</div>
</div>
<script>
// 额外的自定义函数
function updateStatus(orderId, status) {
fetch(`/api/orders/${orderId}/status`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({ status: status })
})
.then(response => response.json())
.then(data => {
console.log('Order updated:', data);
})
.catch(error => console.error('Error:', error));
}
// 监听自定义事件
window.addEventListener('order:created', (e) => {
console.log('Custom event - Order created:', e.detail);
});
window.addEventListener('order:status-updated', (e) => {
console.log('Custom event - Order status updated:', e.detail);
});
window.addEventListener('notification:received', (e) => {
console.log('Custom event - Notification received:', e.detail);
});
</script>
</body>
</html>
启动服务
# 启动Reverb服务 php artisan reverb:start # 启动队列(如果使用队列) php artisan queue:work # 开发环境启动 npm run dev # 生产环境构建 npm run build
路由配置
<?php
// routes/api.php
use App\Http\Controllers\OrderController;
use App\Http\Controllers\NotificationController;
Route::middleware('auth:sanctum')->group(function () {
Route::post('/orders', [OrderController::class, 'store']);
Route::put('/orders/{id}/status', [OrderController::class, 'updateStatus']);
Route::post('/notifications/{id}/read', [OrderController::class, 'markNotificationRead']);
});
测试事件
# 使用Artisan测试事件
php artisan tinker
# 创建测试订单
$order = App\Models\Order::create([
'user_id' => 1,
'customer_name' => 'John Doe',
'customer_email' => 'john@example.com',
'total_amount' => 100.00,
'status' => 'pending',
'items' => json_encode([['product' => 'Test', 'quantity' => 1, 'price' => 100]])
]);
# 这将自动触发OrderCreated事件
注意事项
- 性能优化:大量并发广播时,建议使用Redis队列
- 认证安全:私有频道需要实现授权逻辑
- 重连机制:考虑实现WebSocket断线重连
- 错误处理:实现适当的错误处理和日志记录
- 前端优化:合理使用事件节流和防抖
这个系统实现了实时订单更新、状态变更通知、用户通知等功能,可基于此扩展更多实时功能。