laravel 的 where 是动态查询构造起点,需按场景选对用法:数组传参适用于多字段等值 and;动态条件用 when();in 查询用 wherein() 并校验非空;关联筛选用 wherehas();闭包内须用 $q-> 链式调用。

直接说结论:Laravel 的 where 不是“写一次就完事”的静态语法,而是动态构造查询的起点——用错方式,轻则查不到数据,重则拖垮数据库、暴露 SQL 注入风险。
where 数组传参适合多字段等值 AND 条件
当你要同时按多个字段精确匹配(比如 status = 'active' 且 type = 'premium'),别链式调用 where() 多次,直接传数组:
-
User::where(['status' => 'active', 'type' => 'premium'])->get()生成的是WHERE status = ? AND type = ?,参数绑定紧凑,预编译效率高 - 避免写成
User::where('status', 'active')->where('type', 'premium'),虽然结果一样,但 Query Builder 内部状态切换更多,尤其在循环中叠加时开销明显 - 数组里键名必须是真实字段名,值为具体值;
null会被转成IS NULL,不用手动写whereNull()
动态条件必须用 when() 或手动 if 判断
用户搜索框输入了姓名但没填年龄?筛选条件不能硬编码。靠 when() 最干净:
$query = User::query()->when($request->filled('name'), fn($q) => $q->where('name', 'like', "%{$request->name}%"))->when($request->filled('min_age'), fn($q) => $q->where('age', '>=', (int)$request->min_age))- 比手写
if ($request->has('name')) { $query->where(...); }更简洁,且天然跳过空值逻辑 - 注意用
$request->filled()而不是has():前者排除''、null、[]等 falsy 值,更贴合表单筛选语义
IN 查询必须用 whereIn(),别拼 orWhere()
要查用户所属地区在 ['lga1', 'lga2', 'lga3'] 中的记录?千万别这么写:
- ❌ 错误:
$query->where('lga', 'lga1')->orWhere('lga', 'lga2')->orWhere('lga', 'lga3')—— 一旦前面有其他where,整个逻辑会变成WHERE status = 'active' OR lga = 'lga1' OR ...,完全偏离本意 - ✅ 正确:
$query->whereIn('lga', $lgas),语义清晰,SQL 是标准WHERE lga IN (?, ?, ?) - 务必提前校验数组非空:
!empty($lgas),否则WHERE lga IN ()会报错
关联表字段筛选只能用 whereHas(),不能直接 where()
想查“职业是 teacher 的用户”?别写 User::where('userAttr.job', 'teacher') —— 这会静默失败或返回空,因为 Eloquent 不支持跨表字段直查。
- ✅ 必须用:
User::whereHas('userAttr', fn($q) => $q->where('job', 'teacher'))->get(),底层走EXISTS子查询,语义和性能都稳 - 如果还要把关联数据一起取出来,加
with('userAttr'),但注意:它只影响预加载内容,不参与主模型筛选 - 关系方法名(如
userAttr)必须和模型里定义的函数名完全一致,大小写敏感
最常被忽略的一点:所有带闭包的 where 类方法(whereHas、where 闭包分组、when 闭包)里,必须用 $q->xxx() 链式调用,而不是直接 User::xxx() —— 否则闭包外的上下文就丢了。











