0

0

Laravel本地作用域?局部作用域怎样定义?

畫卷琴夢

畫卷琴夢

发布时间:2025-09-09 09:12:01

|

165人浏览过

|

来源于php中文网

原创

Laravel的本地作用域是通过以scope开头的方法封装可复用查询逻辑,提升代码可读性、维护性和复用性,适用于按需筛选场景,与全局作用域的默认生效不同,本地作用域需显式调用,且可与原生查询方法链式组合,增强查询表达力与灵活性。

laravel本地作用域?局部作用域怎样定义?

Laravel的本地作用域(Local Scopes)本质上就是一种在Eloquent模型中定义可复用查询约束的方法。它允许你将常用的查询逻辑封装起来,让你的模型查询代码更简洁、更具可读性,并且易于维护。简单来说,就是给你的查询起个“别名”,方便日后调用,避免在多个地方重复编写相同的

where
条件。

定义局部作用域非常直接。你需要在Eloquent模型中创建一个方法,方法名以

scope
开头,后面跟着你想要的作用域名称(驼峰命名法)。这个方法会接收一个
$query
实例作为第一个参数。

// app/Models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasFactory;

    /**
     * 筛选活跃用户的本地作用域
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * 筛选特定角色用户的本地作用域
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  string  $role
     * @return \Illuminate\Database\Eloquent::Builder
     */
    public function scopeOfRole($query, $role)
    {
        return $query->where('role', $role);
    }
}

使用时,你只需要在模型实例或模型类上调用这个作用域方法,但不需要加上

scope
前缀。

// 获取所有活跃用户
$activeUsers = User::active()->get();

// 获取所有管理员用户
$admins = User::ofRole('admin')->get();

// 链式调用,获取所有活跃的管理员用户
$activeAdmins = User::active()->ofRole('admin')->get();

我个人觉得,这种方式极大地提升了查询的可读性。你一眼就能看出

User::active()
是在做什么,比写一长串
where('is_active', true)
要清晰得多,尤其是在大型项目中,这种优势会更加明显。它让业务逻辑的表达变得更加自然。

为什么应该优先考虑使用本地作用域,而不是直接编写
where
子句?

这其实是个代码整洁度和维护性的问题。在我看来,本地作用域不仅仅是语法糖,它更是一种设计模式的体现,它鼓励我们把重复的逻辑抽象出来。

  • 代码复用性: 想象一下,如果你需要在十几个地方查询“已发布”的文章,每次都写
    ->where('status', 'published')
    。一旦需求变动(比如“已发布”现在也包括“定时发布”),你就要改十几个地方。而如果用
    scopePublished()
    ,只需要改一个地方——在作用域定义里。这种效率上的提升是巨大的,也大大降低了出错的概率。
  • 可读性与表达力:
    Post::published()->latest()->get()
    读起来就像一句自然语言,非常直观,它明确表达了“获取所有已发布的最新文章”。而
    Post::where('status', 'published')->orderBy('created_at', 'desc')->get()
    虽然也能理解,但在语义上就显得冗余了一些。本地作用域让你的查询意图更加明确,就像给你的查询操作起了个有意义的名字。
  • 可测试性: 封装的查询逻辑更容易进行单元测试。你可以单独测试你的
    scopePublished
    是否正确筛选了数据,而不需要关心它是在哪个控制器或服务中被调用。这让你的测试用例更聚焦,也更容易编写。
  • 避免重复代码: 这是最直接的好处。重复的代码是维护的噩梦,它不仅增加了代码量,也增加了潜在的错误点。本地作用域就是解决这个问题的利器。它强制你将公共的查询逻辑抽象出来,减少了重复造轮子的工作。
  • 业务逻辑的集中: 有时候一个“活跃用户”的定义可能很复杂,涉及多个条件(比如
    is_active
    为真且
    last_login_at
    在最近一个月内)。把这些条件封装在一个作用域里,就相当于把“活跃用户”这个业务概念集中定义了。这对于新加入的团队成员理解整个系统的业务规则非常有帮助,也方便了未来的业务调整。

本地作用域与全局作用域有何不同,各自适用于哪些场景?

这是Laravel查询作用域的另一个重要区分点。虽然它们都旨在简化查询,但应用的时机和粒度完全不同。理解它们的差异,能帮助你做出更合理的技术选型。

AI at Meta
AI at Meta

Facebook 旗下的AI研究平台

下载
  • 本地作用域(Local Scopes): 就像我们前面讨论的,它是一种可选的查询约束。你只有在明确调用它的时候,它才会生效。它的粒度更细,通常用于处理某个特定查询场景下的筛选需求。比如,你可能只在某个页面需要显示“活跃用户”,而在其他页面则不需要。本地作用域赋予了你这种灵活性,你可以根据具体的业务场景来决定是否应用某个查询条件。
    • 适用场景: 用户列表筛选(按角色、按状态)、文章分类(按标签、按发布状态)、商品搜索(按价格区间、按品牌)等等,任何你需要按需应用查询条件的地方。它让你的查询更具上下文相关性。
  • 全局作用域(Global Scopes): 顾名思义,它是一种全局的查询约束。一旦你在模型上定义并应用了全局作用域,那么每次通过该模型查询数据时,这个作用域都会自动生效,除非你明确地将其移除。它的粒度更粗,通常用于处理那些几乎所有查询都应该遵循的普遍性约束。
    • 定义方式: 实现
      Illuminate\Database\Eloquent\Scope
      接口,或者使用闭包。Laravel的软删除(Soft Deletes)功能就是通过全局作用域实现的,它会自动在所有查询中添加
      where('deleted_at', null)
    • 适用场景: 软删除是最好的例子。多租户应用中,根据当前租户ID过滤数据也是一个常见的全局作用域应用。再比如,你可能希望所有对
      Post
      模型的查询都默认只返回
      published
      状态的文章,除非特别指明。这适用于那些“默认行为”应该被改变的场景。
  • 核心区别 本地作用域是“按需选择”,全局作用域是“默认生效”。理解这一点,就能帮你决定何时使用哪种作用域。我个人在使用全局作用域时会非常谨慎,因为它可能会在你不经意间影响到一些查询,导致预期之外的结果,所以一定要确保这个约束是真正“全局”且“普遍”的,并且在必要时知道如何绕过它(使用
    withoutGlobalScope
    withoutGlobalScopes
    )。

如何有效地链式调用多个本地作用域,并与原生查询方法结合?

本地作用域的强大之处之一就在于其出色的链式调用能力。你可以像搭积木一样,将多个作用域和原生的查询构建器方法组合起来,构建出复杂而又清晰的查询。这使得查询语句在保持强大功能的同时,也极具可读性。

  • 链式调用: Laravel的查询构建器设计得非常流畅,每个方法(包括作用域)都会返回一个查询构建器实例,这使得你可以不断地在其上添加更多的条件。

    // 假设我们有User模型,包含active、ofRole和recentlyCreated作用域
    // scopeActive(): where('is_active', true)
    // scopeOfRole($role): where('role', $role)
    // scopeRecentlyCreated(): where('created_at', '>', now()->subDays(7))
    
    $recentActiveAdmins = User::active()
                              ->ofRole('admin')
                              ->recentlyCreated()
                              ->orderBy('name') // 原生查询方法
                              ->limit(10)      // 原生查询方法
                              ->get();

    你看,

    active()
    ofRole('admin')
    recentlyCreated()
    都是本地作用域,它们可以无缝地连接在一起。之后,我们又可以继续接上
    orderBy()
    limit()
    这些Eloquent或查询构建器的原生方法。整个过程就像在写一个句子,非常自然,每一步都清晰地表达了查询的意图。

  • 与原生查询方法的结合: 这几乎是Laravel查询的基石。本地作用域只是为你封装了一部分

    where
    条件,但你仍然可以使用所有查询构建器提供的功能,比如
    select
    join
    groupBy
    having
    union
    等等。本地作用域并不会限制你的查询能力,它只是提供了一个更高级别的抽象层。

    // 结合复杂的where条件,使用闭包进行分组
    $users = User::active()
                 ->where(function ($query) {
                     $query->where('age', '>', 30)
                           ->orWhere('city', 'New York');
                 })
                 ->get();
    
    // 结合join操作,获取已发布文章及其分类名称
    $postsWithCategories = Post::published()
                               ->join('categories', 'posts.category_id', '=', 'categories.id')
                               ->select('posts.*', 'categories.name as category_name')
                               ->get();

    这种灵活性意味着本地作用域不会限制你的查询能力,反而是在此基础上提供了一个更高级别的抽象,让你的代码在保持强大功能的同时,也更加优雅和易于管理。我常常发现,当一个查询变得越来越复杂时,如果能把一些核心的、可复用的逻辑提取成作用域,那么整个查询的结构就会清晰很多,调试起来也方便得多。这是一个我个人非常推崇的实践,它能让复杂的查询逻辑变得更容易理解和维护。

相关专题

更多
laravel组件介绍
laravel组件介绍

laravel 提供了丰富的组件,包括身份验证、模板引擎、缓存、命令行工具、数据库交互、对象关系映射器、事件处理、文件操作、电子邮件发送、队列管理和数据验证。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

316

2024.04.09

laravel中间件介绍
laravel中间件介绍

laravel 中间件分为五种类型:全局、路由、组、终止和自定。想了解更多laravel中间件的相关内容,可以阅读本专题下面的文章。

274

2024.04.09

laravel使用的设计模式有哪些
laravel使用的设计模式有哪些

laravel使用的设计模式有:1、单例模式;2、工厂方法模式;3、建造者模式;4、适配器模式;5、装饰器模式;6、策略模式;7、观察者模式。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

369

2024.04.09

thinkphp和laravel哪个简单
thinkphp和laravel哪个简单

对于初学者来说,laravel 的入门门槛较低,更易上手,原因包括:1. 更简单的安装和配置;2. 丰富的文档和社区支持;3. 简洁易懂的语法和 api;4. 平缓的学习曲线。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

370

2024.04.10

laravel入门教程
laravel入门教程

本专题整合了laravel入门教程,想了解更多详细内容,请阅读专题下面的文章。

81

2025.08.05

laravel实战教程
laravel实战教程

本专题整合了laravel实战教程,阅读专题下面的文章了解更多详细内容。

64

2025.08.05

laravel面试题
laravel面试题

本专题整合了laravel面试题相关内容,阅读专题下面的文章了解更多详细内容。

67

2025.08.05

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

231

2023.09.22

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3.2万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

NumPy 教程
NumPy 教程

共44课时 | 2.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号