eloquent高级查询核心是作用域、访问器与修改器:本地作用域封装常用条件,全局作用域统一约束查询,访问器格式化读取,修改器规范写入,三者协同提升代码可维护性。

在 Laravel 中用好 Eloquent ORM 的高级查询能力,关键不在写多少 SQL,而在于把逻辑收口到模型层——作用域封装条件、访问器统一读取格式、修改器规范写入处理。这三者配合得当,代码更干净,维护成本更低。
本地作用域:让常用查询变简洁
本地作用域是定义在模型里的 public 方法,以 scope 开头,接收一个 Builder 实例并返回它。Laravel 会自动识别前缀,调用时直接用驼峰名。
- 正确写法必须 return $query,否则链式调用中断,比如
User::active()->get()会报错 “Call to a member function get() on null” - 方法不能是 private 或 protected,否则框架找不到
- 带参数的作用域支持灵活过滤,例如:
public function scopeWhereTitle($query, $title) { return $query->where('title', 'like', "%{$title}%"); },调用为Post::whereTitle('Eloquent')->get() - 多个作用域可自由组合:
Post::published()->whereTitle('Scope')->orderBy('created_at')->get()
全局作用域:统一约束所有查询
全局作用域不是模型方法,而是一个独立类,实现 Illuminate\Database\Eloquent\Scope 接口,只须实现 apply() 方法。它会在该模型的所有查询中自动生效,包括 find()、count()、甚至关联预加载的子查询。
- 注册必须放在
booted()静态方法里(不是boot()),确保只执行一次 - 软删除就是靠全局作用域实现的:自动加
where deleted_at is null - 临时跳过全局作用域用
withoutGlobalScopes(),指定某一个则用withoutGlobalScope(ActiveScope::class) - 也可用匿名函数方式注册:
static::addGlobalScope('age', function (Builder $builder) { $builder->where('age', '>', 200); });
访问器与修改器:控制属性的读与写
访问器(get{Attribute}Attribute)在读取属性时触发,修改器(set{Attribute}Attribute)在设置属性时触发。它们操作的是模型属性,不改变数据库原始值,也不参与 SQL 构建。
- 命名基于属性名,不是字段名。例如字段
user_name→ 属性userName→ 访问器getUserNameAttribute - 访问器常用于格式化展示,如状态翻译:
getStatusAttribute($value) { return $value ? '启用' : '禁用'; } - 修改器常用于数据清洗或加密,如邮箱转小写:
setEmailAttribute($value) { $this->attributes['email'] = strtolower($value); },或密码哈希:setPasswordAttribute($value) { $this->attributes['password'] = bcrypt($value); } - 可定义虚拟属性,如拼接姓名:
getFullNameAttribute() { return $this->first_name . ' ' . $this->last_name; },之后直接用$user->full_name
几个容易忽略但很关键的细节
这些点不复杂,但一旦出错就难排查:
- 作用域漏掉
return $query是高频错误,务必检查 - 全局作用域影响所有查询,调试时记得用
withoutGlobalScopes()对比验证 - 访问器/修改器只在 PHP 层生效,
where()查询仍需用原始字段名,不能写where('full_name', ...) - 字段名含下划线(如
is_admin)会转成isAdmin,对应方法名是getIsAdminAttribute,注意大小写匹配











