本地作用域是需显式调用的普通方法,命名须以scope开头加驼峰式,调用时转为短横线;必须返回$query实例,参数需手动校验,不可依赖request或auth,且仅添加查询条件,不影响软删除等全局行为。

本地作用域不是自动生效的“魔法”,它只是模型里一个带固定签名的普通方法,调用时必须显式写出来,漏掉就完全不执行。
scope 方法名和调用名怎么对应
方法名必须以 scope 开头,后面接驼峰命名(如 scopeActive、scopePublishedAfter),Laravel 会自动把驼峰转成短横线风格调用(->active()、->publishedAfter()),但不支持下划线或全小写。
- ✅ 正确:方法叫
scopeLatestPublished(),调用写Post::latestPublished()->get() - ❌ 错误:方法叫
scope_latest_published()或scopepublished(),调用会报Call to undefined method - ⚠️ 注意:
scope是硬性前缀,不能省略;public修饰符也不能少,否则 Eloquent 找不到这个方法
为什么链式调用突然中断了
最常见原因是作用域里漏掉了 return $query。Eloquent 链式调用依赖每个环节都返回 Builder 实例,一旦某个 scope 方法没返回,后续的 ->orderBy()、->paginate() 就会直接报错。
- ✅ 必须写:
return $query->where('status', 'active'); - ❌ 不能写:
$query->where('status', 'active'); // 没 return - ❌ 不能写:
return $this->where(...); // $this 是模型实例,不是 Builder - ⚠️ 带参数的作用域也一样:第一个参数固定是
$query,不能是$this或其他名字
带参数的作用域怎么防崩
参数本身没有类型校验或空值保护,全靠你手动处理。传入非法值(比如 null、空字符串、非数字)很容易在 now()->subDays($days) 这类操作里直接抛异常。
- 显式声明类型(PHP 8+):
public function scopeWithinDays(Builder $query, int $days = 7) - 空值兜底:
$days = $days ?? 7;或if (!$days) { $days = 7; } - 时间参数建议用
Carbon::parse($date)->startOfDay()而不是直接拼字符串 - 别在作用域里读
request()、auth()或$this->id——模型还没实例化,这些都不可用
本地作用域能和关联查询混用吗
可以,而且很常用,但要注意调用位置和语义清晰度。比如想查「有活跃信用记录的客户」,应该在 Credit 模型定义 scopeActive(),再在 Client 的关联里复用,而不是在 Client 里写个 scopeHasActiveCredit() 硬塞 JOIN。
- ✅ 推荐:
Client::with(['credits' => function ($q) { $q->active(); }])->get() - ✅ 也行:
Credit::active()->whereHas('client', fn ($q) => $q->where('is_vip', true))->get() - ⚠️ 少用:
Client::has('credits', '=', 1)->whereDoesntHave('credits', fn ($q) => $q->liquidated())—— 条件一多,可读性断崖下跌
最容易被忽略的是:本地作用域只负责“加条件”,不负责“改行为”。它不会影响软删除、全局作用域或事务隔离级别。如果你发现 ->active() 查不到数据,先确认是不是被 SoftDeletingScope 拦住了,而不是急着重写作用域。


















