深入解析 Laravel Scopes:精简代码,优化查询

admin 2024-01-31 652 阅读 0评论

Laravel 作为一款流行的 PHP 框架,以其丰富的功能著称,能够显著提升 Web 开发效率。Laravel Scopes 便是其中一项实用功能,可用于为 Eloquent 模型定义可重用且可链接的查询约束。本文将深入解析 Laravel Scopes 的概念,并带领您逐步实现它们在 Laravel 项目中的应用。

什么是 Laravel 范围?

Laravel 作用域是一种将查询约束封装为可重用且易于使用的方法的机制。它提供了一种便捷的方式来应用查询条件,有效减少代码重复,并提升代码组织的清晰度。

Laravel 范围的类型:

Laravel 作用域的两种类型:全局作用域局部作用域

全球范围:

全局作用域定义在模型内部,并自动应用于该模型的所有查询。它提供了一种便捷的方式来添加始终应用的通用查询条件。例如,您可以设置一个全局作用域,默认仅检索激活用户或仅检索已发布文章。当您需要全局强制执行某些条件,而不想在每个查询中显式添加它们时,全局作用域非常有用。

生成范围

创建新的全局范围,可以使用php artisan make:scope命令,此命令能生成范围并将其放置在应用程序的app/Models/Scopes目录中:

php artisan make:scope ActiveUserScope

创建全局作用域是一个便捷的过程

1、使用 make:scope 命令生成一个实现 Illuminate\Database\Eloquent\Scope 接口的类。

2、在生成的类中实现 apply 方法。

3、在 apply 方法中,根据需要添加 where 约束或其他类型的子句,以构建所需的查询条件。

<?php
namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Database\Eloquent\Scope;

class ActiveUserScope implements Scope

{

/**

* Apply the scope to a given Eloquent query builder.

*/

public function apply(Builder $builder, Model $model): void

{

$builder->where( column: 'is_active', operator: true);
}

}

应用全局范围

将全局作用域应用于模型的步骤如下:

1、重写模型的 booted 方法。

2、在 booted 方法中,调用模型的 addGlobalScope 方法。

3、将作用域的实例作为 addGlobalScope 方法的唯一参数。

<?php

namespace App\Models;

use App\Models\Scopes\ActiveUserScope;

class User extends Model
{
    protected static function booted()
    {
        static::addGlobalScope(new ActiveUserScope());
    }
}

把上例中的范围添加到 App\Models\User 模型后,再调用 all()get()first() 等方法,将会执行以下 SQL 查询:

select * from `users` where `is_active` = true

匿名全球范围

Eloquent 提供了使用闭包定义全局作用域的灵活性,方便创建不需要专用类的简单作用域。

1、指定自定义作用域名称作为 addGlobalScope 方法的第一个参数。

2、在闭包中,定义作用域的逻辑,通常是添加 where 约束或其他类型的子句。

/**
* The "booted" method of the model.
* @return void
*/
no usages
protected static function booted(): void
{
  static::addGlobalScope( scope: 'activeUser'function (Builder $builder) {
$builder->where( column: 'is_active', operator: true);
});
}

删除全局范围

从特定查询中删除全局作用域,请使用 withoutGlobalScope 方法,此方法将全局范围的类名作为其唯一参数:

User :: withoutGlobalScope ( ActiveUserScope :: class )-> get ();

如果使用闭包定义全局作用域,请提供分配给全局作用域的字符串名称:

User :: withoutGlobalScope ( 'activeUser' )-> get ();

要删除查询的一个或多个全局范围,可以使用withoutGlobalScopes方法:

// 删除所有作用域... 
User :: withoutGlobalScopes ()-> get (); 

// 删除特定范围... 
User :: withoutGlobalScopes ([ 
    ActiveUserScope :: class , 
    HasEmailVerifiedScope :: class
 ])-> get ();

本地范围:

本地范围被定义为模型内的方法,用于在每次使用的基础上应用查询约束。当您想要根据用户输入或其他因素动态应用特定条件时,本地范围非常有用。提供了一种灵活的方式来向查询添加条件,而不会污染控制器/服务/存储库代码。

作用域应始终返回相同的查询构建器实例或void:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
    * @param Builder $builder
    * @return void
    */
    public function scopeEmailVerified(Builder $builder): void
    {
        $builder->whereNotNull('email_verified_at');
    }


    /**
    * @param Builder $builder
    * @return void
    */
    public function scopeActiveUser(Builder $builder): void
    {
        $builder->where('is_active'true);
    }
}

使用本地范围

定义范围后,可以在查询模型时调用其方法。但务必注意,调用该方法时不应包含范围前缀,还可以将调用链接到不同的范围:

use App\Models\User;
 
$users = User::emailVerified()->activeUser()->get();

在 SQL 中:

select * from `users` where `email_verified_at` is not null and `is_active` = true

要使用“or”查询运算符组合多个 Eloquent 模型范围,可能需要使用闭包来实现正确的逻辑分组:

User :: emailVerified ()-> orWhere (function (Builder $query ) { 
    $query -> activeUser (); 
})-> get ();

在 SQL 中:

select * from `users` where (`email_verified_at` is not null or (`is_active` = true));

由于这可能会变得很麻烦,Laravel 提供了一个“更高阶”的orWhere方法,能够流畅地将范围链接在一起,而不需要闭包:

User :: emailVerified ()->orWhere-> activeUser ();

动态范围

有时可能想要定义一个接受参数的范围,只需在作用域方法的签名中包含其他参数即可,范围参数应在 $query 参数之后声明:

/** 
* @param Builder $query 
* @param string $role 
* @return void 
*/ 
public  function  scopeOfRole ( Builder $query , string  $role ): void
 { 
    $query -> where ( 'role' , $role ); 
}

将所需的参数添加到作用域方法的签名后,可以在调用作用域时传递这些参数:

$users = User :: ofRole ( 'admin' )-> get ();

实现 Laravel 范围:

当我们需要定义很多范围时,为了避免类文件过大,我们可以使用 Trait 来将所有范围定义在一个独立的文件中,然后让模型类使用它。

use App\Models\User\UserScopeHelper;

class User extends Model
{
    use UserScopeHelper;

    /** 你的代码 */
}
<?php

declare(strict_types=1);

namespace App\Models\Traits;

use Illuminate\Database\Eloquent\Builder;

trait UserScopeHelper
{

    /**
     * 只查询激活用户
     *
     * @param Builder $builder
     * @return void
     */
    public function scopeActiveUser(Builder $builder): void
    {
        $builder->where('is_active'true);
    }

    /**
     * 只查询已验证邮箱的用户
     *
     * @param Builder $builder
     * @return void
     */
    public function scopeEmailVerified(Builder $builder): void
    {
        $builder->whereNotNull('email_verified_at');
    }

    /**
     * 根据角色查询用户
     *
     * @param Builder $builder
     * @param string $role
     * @return void
     */
    public function scopeOfRole(Builder $builder, string $role): void
    {
        $builder->where('role'$role);
    }
}



为了方便使用范围,请在模型中使用 Trait:

<?php

namespace App\Models;

use App\Models\Traits\UserScopeHelper;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use UserScopeHelper;
}

Laravel 范围的优点:

1、代码可重用性:范围允许您定义一次查询约束并在应用程序的不同部分中重用它们。这有助于消除代码重复并促进更干净、更易于维护的代码。

2、提高可读性:通过将查询约束封装在命名方法中,作用域使代码更具表现力且更易于理解。范围还有助于减少重复查询条件的噪音,从而产生更清晰、更易读的查询。

3、灵活性:Laravel 作用域提供了向查询动态添加条件的灵活性。通过本地作用域,您可以根据不同的条件轻松链接多个作用域方法,使您的代码更能适应不断变化的需求。

结论:

Laravel Scopes 提供了一种强大而优雅的方式来为 Eloquent 模型定义可重用的查询约束。通过将查询条件封装在作用域内,开发人员可以实现更简洁的代码,增强代码的可重用性,并提高 Laravel 应用程序的可维护性。凭借其易于实现和灵活性,Laravel 作用域成为所有 Laravel 开发人员的宝贵工具。

喜欢就支持以下吧
点赞 0

发表评论

快捷回复: 表情:
aoman baiyan bishi bizui cahan ciya dabing daku deyi doge fadai fanu fendou ganga guzhang haixiu hanxiao zuohengheng zhuakuang zhouma zhemo zhayanjian zaijian yun youhengheng yiwen yinxian xu xieyanxiao xiaoku xiaojiujie xia wunai wozuimei weixiao weiqu tuosai tu touxiao tiaopi shui se saorao qiudale qinqin qiaoda piezui penxue nanguo liulei liuhan lenghan leiben kun kuaikule ku koubi kelian keai jingya jingxi jingkong jie huaixiao haqian aini OK qiang quantou shengli woshou gouyin baoquan aixin bangbangtang xiaoyanger xigua hexie pijiu lanqiu juhua hecai haobang caidao baojin chi dan kulou shuai shouqiang yangtuo youling
提交
评论列表 (有 0 条评论, 652人围观)

最近发表

热门文章

最新留言

热门推荐

标签列表