ThinkPHP搜索功能需规避空值过滤、多字段OR模糊查询、时间范围等陷阱:须用array_filter清理无效值,模糊查用'|'分隔字段,复杂条件用搜索器并正确闭包调用,分页需透传query参数。

ThinkPHP 的搜索功能不是靠堆 where 数组就能跑通的,空值、模糊、多字段 OR、时间范围这些常见需求,直接写数组会出错或漏数据。
where 数组里填空值会导致全表过滤
TP6 的 where 数组默认做严格等值匹配,['title' => '', 'status' => null] 会生成 WHERE title = '' AND status = NULL,结果一条不剩。
- 用
array_filter($data, function($v) { return $v !== '' && $v !== null && $v !== [] && $v !== false; }, ARRAY_FILTER_USE_BOTH)过滤掉无效值 - 别依赖默认键重排,
ARRAY_FILTER_USE_BOTH必须加,否则字段名丢了 - 如果要保留零值(比如
score = 0),得单独判断:!is_numeric($v) && !$v不够准,改用strlen((string)$v) === 0更稳妥
多字段模糊查要用 | 分隔,不能塞进数组
where(['title|content|username' => ['like', "%{$keyword}%"]]) 是对的;但写成 ['title' => "%{$keyword}%", 'content' => "%{$keyword}%"] 就是 AND 关系,逻辑不对。
-
|表示 OR,&表示 AND,字段名之间不能有空格 - 如果字段类型不一致(比如
content是 TEXT,title是 VARCHAR),MySQL 可能隐式转码失败,建议统一加COLLATE utf8mb4_unicode_ci(在模型中用->bind(['title' => PDO::PARAM_STR])控制) - 关键词含 SQL 特殊字符(如
%、_)必须先addcslashes($keyword, '%_\')转义,再拼"%{$escaped}%"
复杂条件(如时间区间、状态+类型组合)必须用搜索器
搜索器不是自动触发的,要三步齐备:模型里定义 searchCreateTimeAttr 方法 + 查询时调 withSearch(['createTime'], $params) + 外部传参字段名和方法名严格对应。
立即学习“PHP免费学习笔记(深入)”;
-
searchCreateTimeAttr的$query是闭包,不能直接调$query->where(),得写成$query(function ($builder) use ($value) { $builder->whereBetween('create_time', $value); }); - 多个搜索器共存时,执行顺序固定:先跑所有
where()链式调用,再跑withSearch,所以外部写的where('status', 1)和搜索器里的where('status', 2)会同时生效,变成AND status = 1 AND status = 2,结果为空——得在搜索器里先unset掉外部条件,或改用whereOr - TP6.1+ 支持
withSearch(['createTime'], $params, ['createTime' => false])动态开关某个搜索器,6.0 不支持这个第三个参数
分页时参数丢失?paginate() 的 query 参数必须手动透传
不传 query,翻页后所有搜索条件就清空了。这不是 Bug,是设计如此。
- 控制器里写
UserModel::where(...)->paginate(15, false, ['query' => request()->param()]),注意第二个参数false表示不自动合并 GET 参数 - 如果用了
withSearch,request()->param()里得包含搜索器对应字段(如create_time),不然搜索器收不到值 - 前端表单用
method="get",不要用 POST,否则request()->param()拿不到 URL 参数
最常被忽略的是搜索器内部的闭包调用方式,以及 where 数组对空值的零容忍——这两点不处理,搜索功能看着能跑,一上生产就丢数据。



















