什么是事件
Laravel 的事件系统实现了简单的观察者模式。
在应用中触发(dispatch)事件,再定义响应这些事件的监听器,可以把组件间的耦合降到最小。
例如触发「订单已确认」事件后,「发送确认邮件」「减少库存」「向 Slack 通知」等多个监听器可以独立运行。
订单处理代码完全不需要知道邮件或 Slack 的实现。
事件类放在 app/Events,监听器放在 app/Listeners。
目录不存在时 Artisan 命令会自动创建。
生成事件与监听器
php artisan make:event UserRegistered
php artisan make:listener SendWelcomeEmail --event=UserRegistered
不带参数运行会进入交互式提示:
php artisan make:event
php artisan make:listener
注册事件
事件自动发现
默认情况下,Laravel 会扫描 app/Listeners 自动注册监听器。
它会读取 handle 或 __invoke 方法的参数类型来推断事件映射。
use App\Events\UserRegistered;
class SendWelcomeEmail
{
public function handle(UserRegistered $event): void
{
// 发送欢迎邮件
}
}
使用 PHP union 类型可以让同一方法处理多个事件:
public function handle(UserRegistered|UserUpdated $event): void
{
// ...
}
若监听器放在其他目录,在 bootstrap/app.php 中额外指定扫描位置:
->withEvents(discover: [
__DIR__.'/../app/Domain/Orders/Listeners',
])
通配符可指定多个目录:
->withEvents(discover: [
__DIR__.'/../app/Domain/*/Listeners',
])
查看已注册的监听器:
生产环境建议缓存监听器清单以提升性能。部署时运行 php artisan optimize 或 php artisan event:cache。
清除缓存用 php artisan event:clear。
手动注册
也可以在 AppServiceProvider 的 boot 中通过 Event 门面手动注册:
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(
UserRegistered::class,
SendWelcomeEmail::class,
);
}
也可以用闭包:
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(function (UserRegistered $event) {
// ...
});
}
定义事件
事件类是数据的容器,不含逻辑,只以属性形式承载信息:
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public User $user,
) {}
}
SerializesModels trait 让排队的监听器在序列化事件时能正确处理 Eloquent 模型。
触发事件
使用 dispatch 静态方法或 event() 辅助函数触发事件:
use App\Events\UserRegistered;
// 静态方法
UserRegistered::dispatch($user);
// 辅助函数
event(new UserRegistered($user));
也有按条件触发的方法:
UserRegistered::dispatchIf($condition, $user);
UserRegistered::dispatchUnless($condition, $user);
事务提交后触发
若希望仅在数据库事务提交后触发,让事件类实现 ShouldDispatchAfterCommit。
事务失败则事件会被丢弃:
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered implements ShouldDispatchAfterCommit
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public User $user,
) {}
}
编写监听器
监听器通过 handle 方法接收事件。构造器中的依赖由服务容器自动注入:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail
{
public function __construct() {}
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)
->send(new WelcomeMail($event->user));
}
}
handle 方法返回 false 可以阻止事件继续传给后续监听器。
排队监听器
发送邮件、发起 HTTP 请求等耗时操作可以作为排队监听器异步执行。
只需实现 ShouldQueue,事件触发时监听器会自动入队。
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// 此逻辑由队列 worker 异步执行
}
}
使用排队监听器前需配置队列并启动 worker。详见队列与任务。
自定义队列连接 / 名称 / 延迟
用 PHP 属性设置:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Delay;
use Illuminate\Queue\Attributes\Queue;
#[Connection('redis')]
#[Queue('emails')]
#[Delay(10)]
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// ...
}
}
也可以用方法动态返回:
public function viaConnection(): string
{
return 'redis';
}
public function viaQueue(): string
{
return 'emails';
}
public function withDelay(UserRegistered $event): int
{
return 10;
}
最大重试次数与超时
用 #[Tries]、#[Timeout] 控制失败行为:
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;
#[Tries(3)]
#[Timeout(30)]
class SendWelcomeEmail implements ShouldQueue
{
// ...
}
失败处理
定义 failed 方法处理超过最大重试仍失败的情况:
use Throwable;
public function failed(UserRegistered $event, Throwable $exception): void
{
// 通知管理员等
}
事件订阅者
事件订阅者用来把相关的多个事件处理器组织在一个类中。
创建订阅者
subscribe 方法返回事件与处理函数的映射数组:
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;
class UserActivitySubscriber
{
public function handleLogin(Login $event): void
{
// 登录处理
}
public function handleLogout(Logout $event): void
{
// 登出处理
}
/** @return array<string, string> */
public function subscribe(Dispatcher $events): array
{
return [
Login::class => 'handleLogin',
Logout::class => 'handleLogout',
];
}
}
注册订阅者
若启用事件自动发现,返回数组的 subscribe 会被自动注册。
手动注册在 AppServiceProvider::boot 中调用 Event::subscribe:
use App\Listeners\UserActivitySubscriber;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::subscribe(UserActivitySubscriber::class);
}
实践示例:用户注册时发送欢迎邮件
创建事件类
php artisan make:event UserRegistered
编辑 app/Events/UserRegistered.php 添加用户属性:<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public User $user,
) {}
}
创建监听器
php artisan make:listener SendWelcomeEmail --event=UserRegistered
实现 ShouldQueue,让发邮件异步处理:<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)
->send(new WelcomeMail($event->user));
}
}
在控制器中触发
在用户注册后调用 UserRegistered::dispatch():<?php
namespace App\Http\Controllers\Auth;
use App\Events\UserRegistered;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class RegisterController extends Controller
{
public function store(Request $request): RedirectResponse
{
$user = User::create($request->validated());
UserRegistered::dispatch($user);
return redirect('/dashboard');
}
}
控制器只负责触发事件,不关心邮件发送的实现。
未来若要加上 Slack 通知,控制器无需改动。
启用事件自动发现后无需在 AppServiceProvider 中手动注册。放在 app/Listeners 中的类会被自动识别。
使用 php artisan event:list 查看已注册的事件和监听器,定期检查是否有意外注册。