ThinkPHP 的 where() 方法传数组时默认是 AND 关系,如 where(['status' => 1, 'type' => 'news']) 等价于 WHERE status = 1 AND type = 'news';需 OR 时须用 whereOr() 或闭包。

where() 多条件传数组时的逻辑是 AND 还是 OR?
ThinkPHP 的 where() 方法接收数组时,默认所有键值对之间是 AND 关系,不是 OR。比如 where(['status' => 1, 'type' => 'news']) 等价于 SQL 中的 WHERE status = 1 AND type = 'news'。
容易踩的坑:有人误以为数组能自动区分 OR/AND,结果筛选出空数据却查不出原因。
- 若需 OR,必须显式用
whereOr()或闭包构建(5.1+)或where('status', 1)->whereOr('type', 'admin') - 5.0 版本不支持链式
whereOr(),得用闭包:where(function ($query) { $query->where('status', 1)->whereOr('type', 'admin'); }) - 数组里写表达式(如
['score' => ['>', 80]])仍属 AND 范畴,不影响整体逻辑关系
复合查询中 NULL 值和空字符串怎么安全过滤?
用户输入可能带空字符串、null、或未提交字段,直接塞进 where() 会导致查出意外数据(比如 where('name', '') 匹配所有 name 为空的记录,而非“忽略该条件”)。
正确做法是**手动过滤无效值**,再拼条件:
立即学习“PHP免费学习笔记(深入)”;
- 用
array_filter($params, function($v) { return $v !== '' && $v !== null && $v !== []; })预处理参数 - 对特定字段做语义判断:比如搜索框为空时,压根不该加
name条件,而不是加where('name', '') - 注意
0是有效值,array_filter()默认会过滤掉,需改用回调:array_filter($params, function($v) { return $v !== '' && $v !== null; })
时间范围查询用 between 还是 >= 和
用 between 看似简洁,但实际容易漏掉边界秒数,尤其配合前端日期选择器(只选到天)时,between '2024-01-01' and '2024-01-31' 会丢掉 1 月 31 日全天的数据(因为数据库时间含时分秒,默认是 00:00:00)。
更稳妥的是手动控制右边界:
- 查“2024-01-01 到 2024-01-31”,应写成:
where('create_time', '>=', '2024-01-01')->where('create_time', ' - 用
strtotime('+1 day')动态算右边界,避免硬编码出错 -
between仅适合明确不含时分秒的场景(如年份、月份归档),且字段类型是DATE而非DATETIME
关联模型多表筛选时 where 条件写在哪一层?
在 with(['user' => function($q){ ... }]) 里写的 where,只作用于关联表(user 表),不会影响主表查询。如果想“查 status=1 且 user.level > 5 的文章”,必须把主表条件写在主查询,关联条件写在闭包里。
常见错误是把全部条件都塞进 with 闭包,结果主表没过滤,返回大量无关记录。
- 主表条件:直接链在
select()前,如where('status', 1)->with(...) - 关联表条件:放在
with的闭包里,如with(['user' => function($q) { $q->where('level', '>', 5); }]) - 跨表联合筛选(如 WHERE 主表.id = 关联表.post_id AND 关联表.status = 1)需用
join(),不能只靠with()
复合查询真正麻烦的从来不是语法,而是条件组合时的逻辑优先级和空值语义——少一个 isset() 判断,就可能让整个列表页崩掉。



















