必须通过app(\illuminate\pipeline\pipeline::class)实例化,send($data)传任意类型初始数据,through()传类名或闭包,then()必须传闭包且每个中间件须return $next($passable)。

直接用 app(\Illuminate\Pipeline\Pipeline::class) 启动,别写 Pipeline::send() 或 new Pipeline——否则容器不生效,依赖注入失败、中间件类找不到、$next 参数类型报错全都会冒出来。
怎么正确实例化并传入初始数据
必须走 Laravel 容器解析,否则所有依赖(比如 App\Services\InventoryService)都拿不到:
-
app(\Illuminate\Pipeline\Pipeline::class)是唯一推荐方式;use Illuminate\Pipeline\Pipeline; new Pipeline会跳过容器绑定,app(Pipeline::class)也行但需确保命名空间没被 alias 覆盖 -
send($data)的$data可以是任意类型:Eloquent 模型、数组、DTO 实例,甚至Request对象,Pipeline 不做类型限制 - 不要在
send()之前对$data做深拷贝——Pipeline 默认传递引用,中间件修改的是同一份对象
through() 里传类名还是闭包?关键看复用性和依赖注入需求
传类名才能享受容器自动解析和构造函数注入;闭包适合临时调试或极简逻辑:
- 类必须声明
handle($passable, \Closure $next),漏掉\Closure类型提示在 PHP 8+ 直接报Too few arguments - 闭包里捕获的变量(如
use ($logger))在 pipeline 构建时就固定了,无法运行时切换实例 - 避免混用:
through([CheckInventory::class, function ($order, $next) { ... }])会让维护者困惑“这个闭包要不要 DI?” - 传
[AuthCheck::class, 'handle']合法但冗余,直接写类名更清晰
为什么 then() 必须传闭包,且中间件里 return $next($passable) 不能少
漏掉 return $next($passable) 是最隐蔽的坑——流程静默中断,then() 拿到 null 或上一步意外返回值,毫无报错提示:
-
then()接收的是最终返回值,不是中间态;它不参与管道执行,只在全部中间件跑完后触发 - 每个中间件必须显式调用
$next($passable)才能进下一步;写if (...) return $passable;就等于提前退出,后续全跳过 - 想终止流程?只能
throw new Exception('xxx'),别用return false或return null - 别写
return $next($passable)->save()——$next()返回的是then()的结果,不是模型实例
Pipeline 不是万能胶:这些场景硬套反而更糟
它只适合同步、线性、单输入单输出、步骤间强依赖的流程。其他情况会徒增复杂度:
- 需要发邮件 + 写日志 + 推送通知?用事件(
event(new UserRegistered($user)))或队列,别塞 Pipeline - 步骤顺序不关键(比如“先更新状态再发短信”),普通方法调用更直白,
$user->updateStatus()->sendSms() - HTTP 请求处理?用框架中间件(
app/Http/Middleware),不是手动 Pipeline——两者定位不同 - 步骤耗时(如调第三方 API、读大文件),Pipeline 会阻塞主线程,应拆进队列
真正难处理的是中间件之间数据契约的隐式传递——比如前一个中间件往 $order 加了 ->locked_at 属性,后一个却没检查是否存在。这类问题不会报错,只会在某个环境突然出错,得靠类型注解 + 单元测试兜底。











