withSearch是唯一触发搜索器的方法,必须作为模型静态方法调用,如User::withSearch(['name'], ['name'=>'admin'])->select();搜索器方法名须为searchFieldNameAttr格式,且仅处理白名单字段,不校验类型或报错。

withSearch 是唯一能触发搜索器的方法,其他查询方式(where、find、select)完全无视搜索器定义。
withSearch 必须配合模型方法调用,不能链在 Query 对象后
withSearch 是模型类的静态方法,只能在模型类上调用,比如 User::withSearch(...)。
它不是 Query 实例的方法,所以写成 User::where(...)->withSearch(...) 会报错:Call to undefined method think\db\Query::withSearch()。
-
✅ 正确写法:
User::withSearch(['name', 'status'], ['name' => 'admin', 'status' => 1])->select();
-
❌ 错误写法:
User::where('id', '>', 10)->withSearch([...])->select(); // 报错 -
搜索器方法名必须严格遵循
searchFieldNameAttr格式(驼峰字段名 +Attr后缀),例如:立即学习“PHP免费学习笔记(深入)”;
- 表字段是
create_time→ 方法名是searchCreateTimeAttr - 表字段是
user_name→ 方法名是searchUserNameAttr
- 表字段是
第一个参数是搜索标识数组,只控制哪些字段允许被搜索;第二个参数是实际传入的值,键名必须和搜索标识一致,否则该搜索器不会执行。
搜索器内部不能直接用 $query->where() 硬拼条件
搜索器函数签名是:public function searchNameAttr($query, $value, $data)。
其中 $query 是当前模型的查询对象(think\db\Query 实例),但它的作用域仅限于本次搜索器逻辑——它不累积到外层查询链,除非你显式返回或修改它。
常见错误是写成:
public function searchNameAttr($query, $value, $data) {
$query->where('name', 'like', "%{$value}%"); // ❌ 无效:没返回,也没绑定到主查询
}✅ 正确做法是:
public function searchNameAttr($query, $value, $data) {
$query->where('name', 'like', "%{$value}%"); // ✅ 可以,$query 是引用传递
}ThinkPHP 的 $query 在搜索器中是引用传递,修改即生效。但要注意:不要在搜索器里调用 select() 或 find(),那会提前执行查询。
搜索器不处理未声明的字段,也不校验类型,容易漏掉非法参数
withSearch 的设计初衷就是白名单过滤:只允许你在第一个参数数组里列出的字段参与搜索。
比如你写了 withSearch(['name'], [...]),那么即使传入 ['name' => 'a', 'id' => 123, 'email' => 'x@y'],只有 name 会被处理,其余字段被完全忽略。
- 这能防止前端传
status=999绕过权限直接查管理员数据; - 但也会掩盖问题:比如字段名写错(
user_name写成username),搜索器根本不会触发,也不会报错,结果就是“查不到”,调试时容易卡住; - 如果字段需要类型转换(如时间范围传字符串数组),必须在搜索器里自己做
strtotime或Carbon::parse,框架不帮你转。
withSearch 的白名单机制很干净,但代价是所有搜索逻辑必须收口到模型里——一旦字段语义变复杂(比如“最近7天”要算时间范围,“模糊+拼音首字母”要连表),搜索器方法就会迅速膨胀。这时候不如拆成独立 Service 类,别硬塞进模型。



















