Laravel 的事件系统基于发布-订阅(Pub/Sub)模式,核心由两部分组成:
事件(Event)
:发生了什么事情,例如 UserRegistered(用户注册了)
监听器(Listener)
:监听并响应这个事件,例如 SendWelcomeEmail
你定义事件类 → 触发事件 → 所有关联的监听器会被执行。
本文以 laravel 11 为例讲解
php artisan make:event UserRegistered
会生成文件:
// app/Events/UserRegistered.php
namespace App\Events;
use App\Models\User;
class UserRegistered
{
public function __construct(public User $user) {}
}
php artisan make:listener SendWelcomeEmail --event=UserRegistered
会生成:
<?php
// app/Listeners/SendWelcomeEmail.php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendWelcomeEmail
{
/**
* Create the event listener.
*/
public function __construct()
{
//
}
/**
* Handle the event.
*/
public function handle(UserRegistered $event): void
{
// 访问 $event->user,发送邮件或通知
\Log::info('Send welcome email to ' . $event->user->email);
}
}
在 Laravel 11 中,事件触发后监听器默认是同步执行的,触发事件后 handle() 方法会在同一个请求内执行完毕,只有执行完成后,主程序才继续。
你也可以实现 ShouldQueue
接口让其异步执行:
class SendWelcomeEmail implements ShouldQueue
Laravel 会自动将该监听器推送到队列,事件触发后主请求立即返回,监听器稍后由队列 worker 执行。
默认情况下,Laravel 会扫描应用内的 Listeners 目录自动发现和注册事件监听器。不需要主动做事件和监听的绑定。当 Laravel 发现监听器的类里面有以 handle 或 __invoke 开头的方法,Laravel 会将这些方法注册为监听器,对应的事件为方法签名中的事件类, 如上面监听类中的 public function handle(UserRegistered $event): void
定义为这个监听类监听的事件是 UserRegistered
但若某个事件有多个监听类,我们可以主动注册事件监听,这样监听器会按照定义顺序执序,不需要考虑顺序的情况可以不定义。
注意个点:若像下面文件一样主动注册监听,监听器中handle方法中不要声明要监听的事件(去掉 UserRegistered $event 传参),否则这个监听器会执行两次;laravel 自动发现执行一次,主动注册又执行一次。shouldDiscoverEvents 设为 false 无效,不知道什么原因。
使用 EventServiceProvider
类主动注册事件监听:
// app/Providers/EventServiceProvider.php
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
protected $listen = [
UserRegistered::class => [ # 事件
SendWelcomeEmail::class, # 可添加多个监听器,按从上到下的顺序执行
],
];
注意 Laravel 11 不会自动加载 App\Providers\EventServiceProvider::class
, 需要在 bootstrap/providers.php
文件中手动引入
<?php
// bootstrap/providers.php
return [
App\Providers\AppServiceProvider::class,
App\Providers\EventServiceProvider::class,
];
然后运行:
php artisan event:cache
📌 Laravel 会缓存事件监听映射,提高性能。开发时如果修改监听器注册,需要重新执行该命令。
use App\Events\UserRegistered;
event(new UserRegistered($user));
event()
是 Laravel 全局助手函数,也可以用Event::dispatch()
。
若监听器比较耗时(如发邮件),建议实现:
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
并确保你已启动队列监听器:
php artisan queue:work
[ 用户注册 ]
↓
[ event(new UserRegistered($user)) ]
↓
[ UserRegistered Event ]
↓
[ SendWelcomeEmail Listener ]
↓
[ 邮件发送 / 记录日志 / 通知等 ]
有时,当一个事件注册了多个监听器时,一个监听器执行后你可能希望停止将事件传播到其他监听器。你可以通过从监听器的 handle 方法中返回 false 来做到这一点。
但注意,它只适用于你使用 Event::until()
(或 event()->until()
)方式触发事件的场景。
Laravel 的两种触发方式对返回值的处理不同:
调用方式 | 返回值 | 是否中止监听器传播 |
---|---|---|
event() |
所有返回值数组 | ❌ 否(全部执行) |
Event::dispatch() |
同上 | ❌ 否(全部执行) |
Event::until() |
首个非 null | ✅ 是(中止执行) |
event()->until() |
同上 | ✅ 是 |
在 until()
模式下,监听器返回 false
将停止后续监听器
维度 | Laravel 事件 | Laravel 队列 |
---|---|---|
模式 | 发布-订阅 | 生产者-消费者 |
解耦 | ✅ 非常适合 | ❌ 不是目标 |
异步 | ❌ 默认同步(可配队列) | ✅ 默认异步 |
多任务 | ✅ 支持多个监听器 | ✅ 但每个 Job 单一职责更好 |
典型用途 | 业务流程解耦(如:注册通知多个逻辑) | 长耗时逻辑(如:生成 PDF、发邮件) |
控制性 | ❌ 无优先级、失败处理弱 | ✅ 有 retry/delay/failure 等机制 |