when不是if替代品,而是避免空条件污染查询链的链式条件构建器;它在条件为falsy时不执行闭包,支持多条件独立判断、else分支及use变量导入,但需注意缓存键需手动包含条件。

when 方法不是 if 的替代品,而是链式条件构建器
when 的核心价值在于「避免空条件污染查询链」。比如你写 ->where('status', $status),但 $status 可能为 null 或空字符串,直接执行会查出意外结果;而用 when($status, fn($q) => $q->where('status', $status)),$status 为 falsy 时整个闭包压根不执行,查询对象不受影响。
常见错误是把它当普通 if 用:when(true, ...) 硬编码条件,或在闭包里漏掉返回查询实例($q->where(...) 必须有返回值,否则链式中断)。
- 条件参数支持任意 falsy 值(
null、''、false、0),不只是布尔 - 闭包第一个参数是当前
Builder实例,必须 return 它(Laravel 9+ 支持箭头函数简写,但记得加=>) - 不要在
when闭包里调用get()或first()—— 它只负责组装,不执行
多个 when 连续调用时,顺序和逻辑分组要手动控制
每个 when 是独立判断、独立执行的,不会自动合并成 AND 或 OR。比如:
$query = User::query()
->when($name, fn($q) => $q->where('name', 'like', "%{$name}%"))
->when($active, fn($q) => $q->where('active', 1))
->when($role, fn($q) => $q->where('role_id', $role));
这等价于原生 SQL 的 WHERE name LIKE ? AND active = ? AND role_id = ?,因为三个 when 都追加了 where。但如果你需要「按角色查,否则按部门查」,就得用 when + else:
->when($role,
fn($q) => $q->where('role_id', $role),
fn($q) => $q->where('dept_id', $dept)
)
-
else分支只在主条件为 falsy 时触发,不能省略 - 多个
when之间没有隐式括号,复杂逻辑(如嵌套 OR)得靠where(function ($q) { ... })手动包裹 - 注意字段名冲突:连续
when都操作同一个字段(如都改status),后一个会覆盖前一个,除非你明确用orWhere
when 传闭包做条件判断时,容易忽略作用域和变量传递
当条件逻辑稍复杂,比如要判断数组长度、调用辅助函数,常写成 when(fn() => count($tags) > 0, ...)。但这里有个坑:闭包里的 $tags 默认无法访问外部变量,PHP 会报 Undefined variable。
正确做法是用 use 显式导入:
->when(fn() => count($tags) > 0,
fn($q) => $q->whereHas('tags', fn($sq) => $sq->whereIn('id', $tags)),
fn($q) => $q->whereDoesntHave('tags')
)->use($tags)
- 条件闭包(第一个参数)和执行闭包(第二个)都需要
use($var)才能读取外部变量 - 如果变量是对象且需修改,要用
use(&$var)引用传递 - 别在条件闭包里做重操作(如 DB 查询、文件读取),它会在每次构建查询时执行,可能被调用多次
when 和高级查询组合时,order / limit / with 很少需要条件化,但容易误加
开发者常想「只有搜索时才排序」「列表页才预加载关联」,于是给 orderBy 或 with 套 when。其实大可不必:
-
orderBy多次调用是累加的,->orderBy('id')->orderBy('created_at')会生成ORDER BY id, created_at;即使某次when没触发,也不影响其他排序 -
with同理,没匹配的预加载只是不执行,不报错也不拖慢主查询 - 真正该用
when控制的是「是否加 where 条件」「是否 join 表」「是否用 distinct」这类会改变结果集语义的操作
最易被忽略的是:when 不影响查询缓存键的生成。如果你用 Cache::remember 包裹带 when 的查询,缓存 key 不会因条件变化而不同 —— 必须手动把条件拼进 key 里,否则 $name=null 和 $name='admin' 可能共用同一份缓存。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











