局部作用域是解决重复where条件和可读性问题的刚需工具,必须以scope开头、返回$query,参数需显式传入,不可依赖请求或认证上下文,类型提示Builder不可省略。

局部作用域(Local Scopes)不是语法糖,而是解决重复 where 条件和可读性崩坏的刚需工具。硬写 where('status', 'active')->where('deleted_at', null) 在多个控制器里,漏一个 deleted_at 就可能查出软删除数据。
scope 方法必须返回 $query 且以 scope 开头
定义时名字不带 scope 前缀,调用就会报 Method does not exist;方法体内忘了 return $query,链式调用直接中断,后续 get() 报 Call to a member function get() on null。
- 正确写法:
public function scopeActive(Builder $query): Builder { return $query->where('status', 'active'); } - 错误写法:
public function active(Builder $query) { $query->where('status', 'active'); }(缺 return) - 错误写法:
public function activeScope(Builder $query) { return $query->where('status', 'active'); }(前缀不对)
动态作用域参数必须显式传入,不能默认或依赖全局状态
比如按时间范围筛选,scopeCreatedBetween($query, $start, $end) 的两个参数必须由调用方提供。Laravel 不会自动填充 now() 或从 request 取值——那是业务逻辑,不该混进模型层。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 安全做法:
$start = $start ?? now()->subMonth(); $end = $end ?? now();在作用域内部做兜底 - 危险做法:在作用域里直接写
request()->input('date')或auth()->id()—— 模型不该感知请求或认证上下文 - 注意:
whereBetween对null敏感,传空字符串会生成WHERE created_at BETWEEN '' AND '',数据库可能报错或返回空结果
本地作用域可以链式组合,但顺序影响 SQL 生成
多个作用域调用顺序即 SQL WHERE 子句的拼接顺序,大多数情况不影响结果,但在涉及 orWhere 或子查询时,顺序错可能导致逻辑错误。
- 推荐写法:
Post::published()->byAuthor(123)->get()(清晰表达“已发布 + 指定作者”) - 避免嵌套 where 逻辑外溢:
scopePublished里别用orWhere,除非你明确知道它会和上一个作用域的条件做 OR 而非 AND - 复杂组合建议抽成独立方法或使用查询构建器对象,而不是堆砌作用域
不要把权限判断塞进本地作用域
本地作用域仍是模型层代码,它不感知当前用户角色。把 where('tenant_id', auth()->user()->tenant_id) 写进 scopeForCurrentTenant,看似方便,实则埋雷:
- 命令行任务、队列、测试环境调用时,
auth()为空,抛异常 该作用域无法被单元测试隔离验证,因为强依赖运行时 auth 状态
- 真正该放的地方是服务类或策略类,模型只管“数据结构”,不管“谁能看到”
最容易被忽略的一点:作用域方法签名里的 Builder 类型提示不是可选的——PHP 8+ 启用严格类型后,漏写会导致运行时报致命错误,而且 IDE 无法帮你补全链式方法。

















