深入解析 Laravel Scopes:精简代码,优化查询
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 开发人员的宝贵工具。
上一篇:LaraUtilX:让 Laravel 开发更简单、更高效
栏 目:Laravel
下一篇:Laravel 数据交互进阶:访问器和修改器实战指南
本文标题:深入解析 Laravel Scopes:精简代码,优化查询
本文地址:https://www.wqjdym.top/Laravel/191.html