thinkphp 8.0模型scope必须返回$query,否则链式调用中断;仅支持user::hot(7)或user::scope('hot',7)两种传参,不兼容数组键名;全局作用域与局部scope条件and叠加,顺序影响order/group但不影响where逻辑。

ThinkPHP 8.0 的模型查询范围(scope)和 TP6/TP7 基本一致,但去掉了部分旧版兼容逻辑,调用更严格、参数校验更明确——不返回 $query 就直接中断链式,传参错位也不会静默忽略。
scope 方法必须返回 $query,否则链式调用失败
这是最常踩的坑:写了个 scopeHot 却忘了 return $query,结果调用 ->hot()->select() 时抛出 Fatal error: Call to a member function select() on null。
-
scope方法内部只负责拼条件,不能执行select()、find()或任何最终查询操作 - 方法签名必须是
public function scopeXxx($query, $param = null),且第一行或最后一行必须有return $query - 如果用了
if分支,每个分支都得返回$query,漏一个就崩
->hot(7) 和 ->scope('hot', 7) 效果一样,但不能混用数组键名传参
TP8.0 不支持 Laravel 风格的 ->scope(['hot' => 7]) 写法。它只认两种传参方式:
-
User::hot(7)->select()→ 第二个参数7直接传给scopeHot($query, $days)的$days -
User::scope('hot', 7)->select()→ 等价于上一条,只是显式写法 - 写成
User::scope(['hot' => 7])会报Invalid argument supplied for foreach()或静默失效(取决于底层实现)
全局作用域 base() 和局部 scope 叠加时,SQL 是 WHERE (base 条件) AND (scope 条件)
比如模型里定义了:
protected function base($query)
{
$query->where('status', 1);
}
public function scopeDeleted($query)
{
$query->where('delete_time', null);
}
那么 User::deleted()->select() 实际生成的 SQL 是:
WHERE status = 1 AND delete_time IS NULL
- 这意味着:如果
base()已锁死status = 1,你又在scope里写where('status', 2),整条查询永远为空 - 调试时用
User::deleted()->buildSql()看真实 SQL,比猜条件组合靠谱得多 - 临时跳过全局作用域,用
User::withoutGlobalScope('base')->deleted()->select()
多个 scope 链式调用顺序影响 AND 条件排列,但不影响结果语义
User::hot()->published()->limit(10)->select() 和 User::published()->hot()->limit(10)->select() 生成的 SQL 中 WHERE 子句顺序可能不同,但逻辑等价。
- 不过,如果某个
scope里写了order或group,顺序就关键了——后写的会覆盖先写的 - 避免在不同
scope里重复设置同一字段(如都写->field('id,name')),TP8 不合并字段,后者会完全替换前者 - 想安全组合,优先用一个带多参数的
scopeSearch($query, $params)统一收口
真正容易被忽略的是:TP8.0 默认关闭了模型的自动字段过滤(比如 hidden 在 scope 内不生效),所有字段拼装都在 select() 执行前完成,所以 scope 里加 ->field() 要格外小心字段遗漏。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











