Laravel 作用域必须以 $query 为首个参数,否则无法注入查询构建器;参数默认值与空值需手动处理;多参数调用严格依赖位置顺序,不支持数组解包或命名参数。

动态作用域不是“写个带参数的方法”就完事了,关键在于参数怎么进、条件怎么判、链式怎么保——漏掉任一环,调用时就会静默失败或返回空结果。
scope方法必须以$query为首个参数
这是 Laravel 自动注入机制的硬性约定。如果写成 scopeActive($status, $query) 或省略 $query,Laravel 根本不会把查询构建器传进来,后续所有 where 都会报错或直接忽略。
- 正确签名:
scopePublished($query, $status = 'published') - 错误签名:
scopePublished($status, $query)(参数顺序错) - 错误签名:
scopePublished($status)(缺$query,调用时无报错但条件不生效)
参数默认值和空值处理得自己兜底
Laravel 不做任何参数预处理。传 null、空字符串、未定义键的数组,都会原样进方法体,不校验也不跳过。
- 用
$date = $date ?? now()->subDays(7)替代裸用$date - 对数组参数用
isset($filters['type'])或Arr::has($filters, 'type')显式判断 - 时间类参数建议统一走
Carbon::parse($date)->startOfDay(),避免字符串比较出错
多参数作用域要小心调用语法
作用域只支持一个 $query + 任意数量后续参数,但调用时不能传数组解包,也不能用命名参数——全靠位置顺序。
- 定义:
scopeFilter($query, $minPrice, $maxPrice, $category) - 正确调用:
->filter(100, 500, 'electronics') - 错误调用:
->filter(['minPrice' => 100])(参数类型错) - 错误调用:
->filter(100, null, 'electronics')(没处理null,可能生成WHERE price )
trait 复用时注意模型字段一致性
把 scopeActive 提到 trait 里很常见,但一旦多个模型的「启用状态」字段名不同(比如 is_active / status / enabled),直接复用就会查错表。
- 方案一:trait 方法加字段名参数,如
scopeActive($query, $field = 'status') - 方案二:在模型中重写作用域,内部调用 trait 的私有方法并指定字段
- 最易踩坑点:trait 中硬编码了
where('status', 1),而某个模型实际用的是active字段,上线后部分数据查不到
动态作用域真正的复杂点不在定义,而在它和调用上下文的耦合——参数来源是否可信、字段是否存在、链式中间有没有被其他作用域干扰,这些都不会报错,只会让结果变少或变错。


















