thinkphp 查询范围(scope)是注入条件而非调用方法,必须声明为 public static,参数仅支持位置或关联数组,多 scope 组合顺序影响 sql 条件嵌套,且不作用于 update/delete。

ThinkPHP 的查询范围(scope)不是“调用方法”,而是“注入条件”——写错签名、漏传参数、混用全局作用域,十次有八次查不到数据。
scope 方法必须是 public static,且第一个参数是 $query
TP5.1+ 强制要求 scope 必须声明为 public static,否则调用时静默失效,不会报错但条件不生效。
- 错误写法:
public function scopeHot($query)→ 模型实例方法,->scope('hot')会找不到 - 正确写法:
public static function scopeHot($query)→ 静态方法,框架能识别并绑定查询对象 - 方法体内不能
return数组或值,只能操作$query并隐式返回(框架内部会处理链式) - 如果用了
return $query->where(...)没问题;但写成return ['id' => 1]就彻底断链
传参方式只有两种:位置参数 or 关联数组,别乱套
scope 支持传参,但 ThinkPHP 不解析键名语法(比如 ->scope(['hot' => 30])),除非你主动适配。
-
User::scope('hot', 30)→ 第二个参数直接传给scopeHot($query, $days)的$days -
User::scope(['hot' => 30])→ 要求scopeHot($query, $params = []),然后取$params['hot'] - 写了
scopeHot($query, $days)却只调用->scope('hot'),$days是null,可能触发 SQL 错误(如WHERE created_at >= NULL) - 别用下划线命名作用域(如
scope_recent),调用时得写->scope('recent'),但方法名必须驼峰才能被自动映射
多个 scope 组合时,顺序决定 AND 条件的嵌套层级
SQL 中的 AND 看似无序,但当 scope 内部含 whereTime、whereExists 或子查询时,执行顺序会影响结果。
-
User::scope('published')->scope('recent')->select():先加 status=1,再加时间过滤 -
User::scope('recent')->scope('published')->select():先算时间范围,再筛状态 —— 如果时间范围内没满足 status=1 的记录,就空了 - 用
User::buildSql()看生成的 SQL,确认条件是否按预期包裹,尤其注意括号位置 - 避免在不同 scope 里重复设置同一字段(如两个 scope 都写
$query->where('status', 1)),会导致status = 1 AND status = 1,虽不报错但冗余
和全局作用域(base / globalScope)叠加时,冲突比想象中更隐蔽
全局作用域是“默认自带”的条件,scope 是“手动追加”的条件,两者合并后,SQL 是 WHERE (全局) AND (scope),而不是“覆盖”。
- 模型定义了
protected function base($query) { $query->where('status', 1); } - 又写了
public static function scopeDraft($query) { $query->where('status', 0); } - 调用
User::scope('draft')->select()→ 实际 SQL 是WHERE status = 1 AND status = 0,永远为空 - 想临时禁用全局作用域,用
User::withoutGlobalScope()(TP6)或User::useGlobalScope(false)(TP5) - TP6 还支持精细控制:
User::withoutGlobalScope(['status']),只关掉某一个全局条件
最易忽略的是:scope 只对 select() 和 find() 生效,调用 update()、delete() 时不走 scope 流程 —— 如果你指望 ->scope('draft')->delete() 删除草稿,它只会删所有行(除非你额外在 delete 前手动加 where)。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











