中间件管请求流、事件管业务动作、模型事件管数据层钩子:中间件仅作用于HTTP生命周期,用于鉴权等;事件用于解耦业务通知,如注册后发邮件;模型事件如beforeWrite用于字段处理,三者边界需严格区分。

ThinkPHP 的中间件和事件驱动机制看似相似,实则定位截然不同:中间件管“请求流”,事件管“业务动作”。混淆二者会导致权限校验失效、数据处理错位或系统难以调试。
中间件只在HTTP请求链中起作用
中间件本质是管道过滤器,仅响应 从入口 index.php 开始、到响应输出结束 这一整条 HTTP 生命周期。它无法感知模型 save()、select() 等内部调用,也不能拦截 service 层方法。
- 正确用法:路由绑定鉴权中间件,如
Route::post('article', 'ArticleController@store')->middleware('auth') - 错误写法:在中间件里直接调用
ArticleModel::create($data)——这绕过了控制器和请求上下文,Session、Auth 状态可能未初始化 - 注意 handle() 必须返回 Response 实例,
return $next($request)是放行唯一方式;返回 null、字符串或不 return,请求会静默中断
事件用于解耦业务动作的后续响应
事件是典型的观察者模式实现,适用于“某件事发生了,需要通知多个模块做各自的事”。它不介入流程控制,只负责广播与响应。
- 典型场景:用户注册成功后,触发
event('UserRegister', $user),由不同监听器分别执行发送邮件、初始化积分、推送消息等操作 - 事件类非必需,多数情况用字符串标识(如
'UserLogin')即可;需携带结构化数据时再定义事件类 - 监听器注册在
app/event.php的listen数组中,支持一个事件绑定多个监听器,顺序执行
模型事件是数据层专用生命周期钩子
模型事件(如 beforeWrite、afterRead)不是通用事件系统的一部分,而是 think\Model 基类在核心方法中硬编码调用的固定节点。
立即学习“PHP免费学习笔记(深入)”;
-
beforeWrite()在save()前执行,适合字段标准化、内容转义,必须直接修改$this->data,返回值无效 -
afterRead()在查询结果转为对象后触发,适合脱敏(如隐藏手机号),但不能改原始数据,否则下次save()会误覆盖 - 无需注册,只要在模型类中定义对应 public 方法,框架自动识别并调用
三者协同的关键落地原则
真实项目中它们常配合使用,但边界必须清晰:
- 权限控制、日志记录、跨域头设置 → 放中间件
- 用户行为归因、异步通知、审计留痕 → 放事件监听器
- 字段格式统一、敏感数据加解密、写入前合规校验 → 放模型事件
- 若需在模型操作前做权限判断,应在控制器中完成(如
Auth::check()),而非试图在模型事件里查 Session 或 Token



















