Hyperf 3.0 的 orWhere 不自动分组,易致 AND/OR 优先级错误;须用 where 闭包显式包裹 orWhere 条件,确保 SQL 括号正确,动态场景需统一构建,调试时用 toRawSql() 验证。

Hyperf 3.0 的 ORM(基于 Eloquent 风格的 Db 和 Model)在多条件拼接时,orWhere 的行为与 Laravel/ThinkPHP 高度相似——它不会自动与前一个 where 组成括号分组,而是平铺为同级 OR 条件。这极易导致 AND/OR 优先级错乱,产生逻辑错误结果。
核心问题:orWhere 破坏主过滤条件
当混合使用 where 和 orWhere 时,Hyperf 默认生成扁平 SQL,不加括号。例如:
User::where('status', 'active')->where('deleted_at', null)->orWhere('role', 'admin')->get()- 实际 SQL:
WHERE status = 'active' AND deleted_at IS NULL OR role = 'admin' - 后果:只要
role = 'admin',哪怕status ≠ 'active'或已软删除,也会被查出
正确写法:用闭包显式分组
必须将所有 OR 相关条件包裹在 where() 闭包中,强制生成括号:
User::where('status', 'active')<br> ->where(function ($query) {<br> $query->where('name', 'like', '%john%')<br> ->orWhere('email', 'like', '%john%');<br> })->get();- 生成 SQL:
WHERE status = 'active' AND (name LIKE '%john%' OR email LIKE '%john%') - 语义清晰,主条件不受干扰
动态条件场景下的安全处理
表单搜索等需动态追加 OR 条件时,避免循环中直接调用 orWhere:
- ❌ 错误:
foreach ($keywords as $kw) { $query->orWhere('title', 'like', "%$kw%"); } - ✅ 正确:先收集条件,在闭包内统一构建
$query->where(function ($q) use ($keywords) {<br> foreach ($keywords as $i => $kw) {<br> if ($i === 0) {<br> $q->where('title', 'like', "%$kw%");<br> } else {<br> $q->orWhere('title', 'like', "%$kw%");<br> }<br> }<br>});
调试与验证建议
Hyperf 提供 toRawSql() 方法(或配合 DB::listen()),务必在关键查询后检查生成的 SQL:
echo User::where(...)->toRawSql(); // 查看真实 SQL 结构- 重点确认:OR 条件是否被括号包裹、AND 主条件是否仍在最外层
- 对模糊搜索字段(如 name/email)考虑添加索引或全文索引,避免因括号分组导致全表扫描


















