欢迎来到我的资源网!
阿里云推广
阿里云推广

Laravel

当前位置:首页 > 网络编程 > Laravel >

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

时间:2024-02-01 13:12:42|栏目:Laravel|点击: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 开发人员的宝贵工具。

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:1435567186 | 邮箱:admin@wqjdym.top

Copyright © 2016-2025我的资源网 版权所有 鲁ICP备18026231号-3