laravel事件系统用于解耦旁路动作,如订单创建后发短信、写日志等;应以“发生了什么事”定义事件(如ordercreated),用监听器(如sendsmsnotification)处理;需注意队列配置、数据显式传递、避免请求/会话依赖,并区分强一致性场景是否适用。

Laravel 的事件系统不是“必须用”,而是“该用时不用就容易耦合”——比如订单创建后发短信、写日志、更新库存,全塞在控制器里,改一个逻辑就得动整个方法。
怎么注册事件和监听器
别先写代码,先想清楚:这个动作是不是“发生了什么事”,而不是“我要做什么”。比如 OrderCreated 是事件,SendSmsNotification、LogOrderActivity 才是监听器。
- 用
php artisan make:event OrderCreated生成事件类,它默认是空的,只负责携带数据(比如$order实例) - 监听器用
php artisan make:listener SendSmsNotification --event=OrderCreated,这样会自动在EventServiceProvider的$listen数组里配好映射 - 手动注册也行,但别漏掉
EventServiceProvider::boot()里的Event::listen(...)调用,否则监听器根本不会被触发
事件触发时为什么监听器没执行
最常见的是队列配置问题:你写了 dispatch(new OrderCreated($order)),但监听器没跑,大概率是因为事件没进队列,或者队列根本没跑。
- 检查
config/events.php里的'default' => 'sync'—— 如果是sync,监听器会同步执行;改成redis或database就要确保队列服务已启动(php artisan queue:work) - 监听器类里如果用了
ShouldQueue接口,但没配队列驱动,它就静默失败,不会报错 - 事件类没实现
ShouldBroadcast却调用了broadcast(),也会中断流程,但错误可能被吞掉
监听器里访问请求或 Session 会出问题
因为事件可能异步执行,而 request()、session() 在队列任务里不可用——它们绑定的是原始 HTTP 请求生命周期,队列进程是独立的 PHP 进程。
- 必须把需要的数据显式传进事件构造函数,比如
new OrderCreated($order, $userId, $ip),别在监听器里临时去查 - 不要在监听器里调
auth()->user(),用户可能已登出,或根本没登录上下文;改用事件携带的$userId去查用户 - 日志里想记请求路径?得提前存到事件属性里,
$this->url = request()->fullUrl(),不然队列里拿不到
什么时候不该用事件系统
不是所有“之后要做点什么”的场景都适合事件。核心判断标准是:这件事是否和当前业务主流程强相关、是否要求强一致性、是否需要事务回滚联动。
- 扣减库存必须和订单创建在同一个事务里,用事件 + 队列就可能超卖——这时候应该用数据库事务+锁,而不是
OrderCreated - 给用户发欢迎邮件可以异步,但修改用户邮箱后立刻发验证邮件,最好同步发,避免用户以为没生效
- 监听器里再抛出未捕获异常,会导致整个队列任务失败卡住,得加
try/catch并记录错误,不能依赖框架兜底
事件真正的价值不在“解耦”这个词本身,而在让你能清晰区分“核心路径”和“旁路动作”。一旦开始往监听器里塞数据库写操作、API 调用、甚至重试逻辑,就说明边界已经模糊了——这时候该回头看看,是不是把本该在领域层做的事,丢给了事件系统硬扛。










