跳转到主要内容

什么是作用域

Eloquent 的作用域是将查询约束打包并可复用的机制。作用域分两类。

本地作用域

定义

本地作用域通过给模型方法加上 #[Scope] 属性来定义。
#[Scope] 属性位于 Illuminate\Database\Eloquent\Attributes 命名空间,是 PHP 8.0 之后的原生语法。

使用

已定义的作用域可以像方法一样调用,也可以链式使用。

传递参数

可以在作用域方法的第 2 个参数之后定义额外参数。
调用时直接传参即可。

orWhere 的组合

orWhere 连接作用域时,有时需要逻辑分组。

全局作用域

机制

全局作用域是实现了 Illuminate\Database\Eloquent\Scope 接口的类。该接口仅要求实现 apply 一个方法。
apply 方法中给查询构造器附加约束。

创建全局作用域类

使用 make:scope 命令生成模板。
将生成 app/Models/Scopes/ActiveScope.php

应用到模型

1

使用 #[ScopedBy] 属性注册(推荐)

Laravel 13 中使用 #[ScopedBy] 属性最简洁。
可以将多个作用域以数组形式指定。
2

在 booted() 方法中手动注册

也可以覆写 booted 方法并调用 addGlobalScope
添加全局作用域后,User::all() 等所有查询都会自动附加 WHERE is_active = 1

匿名闭包形式的全局作用域

对于不需要单独放在文件里的简单作用域,可以用闭包定义。
要移除以闭包定义的作用域,必须使用作用域名(字符串),而不是类名。

移除全局作用域

在特定查询中你可能希望禁用作用域。

框架内部:SoftDeletingScope

看看 Laravel 标准的 SoftDeletes trait 如何使用全局作用域,就能了解实现模式。 SoftDeletingScope 实现了 Scope 接口。
withTrashed() 实际上是调用 withoutGlobalScope($this)。也就是说,通过移除 SoftDeletingScope 自身,让被删除的记录也能被取到。
onlyTrashed() 同样先移除自身作用域,再附加 whereNotNull('deleted_at')
Scope 接口未定义 extend 方法,但 Eloquent 的 Builder 会自动调用作用域上存在的 extend 方法。要添加自定义宏时可以利用它。

实战用例

多租户:按租户 ID 自动过滤

SaaS 应用中,对所有查询自动加上租户 ID 过滤非常重要。
这样只需调用 Post::all(),就能得到当前登录用户所在租户的数据。

公开/非公开筛选

管理后台希望显示未发布的文章,前台仅显示已发布文章的场景。
在管理后台用 withoutGlobalScope 移除作用域。

请使用 addSelect 而非 select

在全局作用域中添加列时请使用 addSelect 而不是 select。使用 select 会覆盖调用方查询已选择的列。

下一步

Eloquent 自定义类型转换

将属性转换逻辑实现为自定义 Cast,并结合 Value Object 模式使用。
最后修改于 2026年7月18日