Laravel 的事件系统提供了一种优雅的方式来实现观察者模式,让你可以在应用中定义和触发事件,并通过监听器来响应这些事件。这种设计可以显著解耦业务逻辑,使代码更易于维护和扩展。本章将全面讲解如何创建事件、注册监听器、触发事件,以及如何利用队列和事件订阅者处理复杂场景。
在 Laravel 中,事件系统的基本流程如下:
UserRegistered),通常存放于 app/Events 目录。SendWelcomeEmail),存放于 app/Listeners 目录。EventServiceProvider 的 $listen 数组中注册事件与监听器的映射。event(new UserRegistered($user)) 触发事件。Laravel 提供了 Artisan 命令快速生成事件和监听器。
php artisan make:event UserRegistered
这会在 app/Events/UserRegistered.php 创建事件类。通常事件类只需包含相关数据,例如:
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
}
php artisan make:listener SendWelcomeEmail --event=UserRegistered
生成的监听器位于 app/Listeners/SendWelcomeEmail.php,其中包含 handle 方法,该方法接收事件对象作为参数:
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail
{
public function handle(UserRegistered $event)
{
// 发送欢迎邮件
Mail::to($event->user->email)->send(new WelcomeMail($event->user));
}
}
在 app/Providers/EventServiceProvider.php 的 $listen 数组中定义事件与监听器的关系:
protected $listen = [
\App\Events\UserRegistered::class => [
\App\Listeners\SendWelcomeEmail::class,
\App\Listeners\AssignDefaultRole::class,
// 可以为一个事件添加多个监听器
],
];
注册后,使用 php artisan event:cache 可缓存事件映射(生产环境推荐),开发环境也可以不缓存,Laravel 会自动扫描。
使用全局辅助函数 event() 或 Event Facade 触发事件:
use App\Events\UserRegistered;
// 在控制器或任意位置
$user = User::create([...]);
event(new UserRegistered($user));
// 或使用 Facade
use Illuminate\Support\Facades\Event;
Event::dispatch(new UserRegistered($user));
触发事件后,Laravel 会依次调用所有注册的监听器的 handle 方法。
如果某个监听器执行耗时操作(如发送邮件、调用外部 API),可以将其放入队列,避免阻塞主请求。只需让监听器类实现 ShouldQueue 接口:
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
{
// ...
}
Laravel 会自动将监听器推送到队列。你还可以在监听器中设置队列连接、延迟等:
public $queue = 'emails'; // 指定队列名称
public $delay = 60; // 延迟60秒执行
SerializesModels trait,以确保模型对象能被正确序列化。Laravel 会使用模型的主键来重建实例,避免序列化完整模型。
当同一个类需要监听多个事件时,可以使用事件订阅者来组织逻辑。生成订阅者:
php artisan make:listener UserEventSubscriber --event
订阅者类需包含 subscribe 方法,在其中注册事件监听:
namespace App\Listeners;
use Illuminate\Events\Dispatcher;
class UserEventSubscriber
{
public function handleUserRegistered($event) { ... }
public function handleUserLogin($event) { ... }
public function subscribe(Dispatcher $events)
{
$events->listen(
'App\Events\UserRegistered',
[UserEventSubscriber::class, 'handleUserRegistered']
);
$events->listen(
'App\Events\UserLogin',
[UserEventSubscriber::class, 'handleUserLogin']
);
}
}
然后在 EventServiceProvider 的 $subscribe 属性中注册订阅者:
protected $subscribe = [
\App\Listeners\UserEventSubscriber::class,
];
Laravel 自身会触发许多事件,例如 Illuminate\Auth\Events\Login、Illuminate\Auth\Events\Registered、Illuminate\Queue\Events\JobProcessed 等。你可以监听这些事件来实现扩展功能。例如,在用户登录后记录日志:
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
];
Laravel 模型也有自己的事件系统(如 created、updated、deleted)。模型事件适用于与 Eloquent 模型生命周期相关的操作,而通用事件系统更适合跨模块的业务事件。你可以根据需要选择。
// 模型事件示例(在模型中定义)
protected static function booted()
{
static::created(function ($user) {
// 用户创建后执行
});
}
OrderPaid、UserLoggedIn,清晰表达发生的事情。failed 方法处理失败的队列任务,或配置重试次数。php artisan event:cache 提升性能,开发环境修改事件映射后需重新缓存。Event::fake() 在测试中断言事件是否被触发。
use Illuminate\Support\Facades\Event;
use App\Events\UserRegistered;
public function test_user_registered_event_is_dispatched()
{
Event::fake();
// 执行注册逻辑
$response = $this->post('/register', [...]);
Event::assertDispatched(UserRegistered::class);
}
Laravel 的事件系统为应用提供了灵活的扩展点,通过事件和监听器,你可以将核心业务逻辑与辅助操作(如日志、通知、缓存更新)解耦。结合队列,你可以异步处理耗时任务,提升应用响应速度。掌握事件系统是构建大型 Laravel 应用的关键技能之一。