Mohamd-Fouladgar/ eloquent-builder
Requires
- php: ^8.2
- illuminate/database: ^10.0|^11.0
- illuminate/support: ^10.0|^11.0
Requires (Dev)
- mockery/mockery: ^1.4
- orchestra/testbench: ^7.0|^8.0
- php-coveralls/php-coveralls: ^2.1
- phpunit/phpunit: ^10.5.17
README
此包允许您根据传入的请求参数构建优雅的查询。这极大地简化了查询和条件,将使您的代码更清晰、易于维护。
版本兼容性
基本用法
假设您想获取以下请求参数的用户列表
//Get api/user/search?age_more_than=25&gender=male&has_published_post=true [ 'age_more_than' => '25', 'gender' => 'male', 'has_published_post' => true, ]
在常见的实现中,预期以下代码将执行
<?php namespace App\Http\Controllers; use App\Models\User; use Illuminate\Http\Request; class UserController extends Controller { public function index(Request $request) { $users = User::where('is_active', true); if ($request->has('age_more_than')) { $users->where('age', '>', $request->age_more_than); } if ($request->has('gender')) { $users->where('gender', $request->gender); } // A User model may have an infinite numbers of Post(One-To-Many). if ($request->has('has_published_post')) { $users->where(function ($query) use ($request) { $query->whereHas('posts', function ($query) use ($request) { $query->where('is_published', $request->has_published_post); }); }); } return $users->get(); } }
但是 在使用 EloquentBuilder 之后,上述代码将变为以下形式
<?php namespace App\Http\Controllers; use App\User; use EloquentBuilder; use Illuminate\Http\Request; class UserController extends Controller { public function index(Request $request) { return EloquentBuilder::model(User::class) ->filters($request->all()) ->thenApply() ->get(); } }
您只需为要添加到查询中的每个参数定义一个过滤器。
安装
您可以通过 composer 安装此包
composer require mohammad-fouladgar/eloquent-builder
警告:
Lumen
框架不再受支持!
过滤器命名空间
所有过滤器的默认命名空间为 App\EloquentFilters
,其基础名称为模型。例如,对于 User
模型,过滤器命名空间将为 App\EloquentFilters\User
。
├── app
├── Console
│ └── Kernel.php
├── EloquentFilters
│ └── User
│ ├── AgeMoreThanFilter.php
│ └── GenderFilter.php
└── Exceptions
└── Handler.php
通过配置文件自定义
您可以选择使用以下命令发布配置文件
php artisan vendor:publish --provider="Fouladgar\EloquentBuilder\ServiceProvider" --tag="config"
并设置您模型的过滤器命名空间,它们将位于
return [ /* |-------------------------------------------------------------------------- | Eloquent Filter Settings |-------------------------------------------------------------------------- | | Here you should specify default all you Eloquent Model Filters. | */ 'namespace' => 'App\\EloquentFilters\\', ];
定义一个过滤器
编写过滤器很简单。定义一个类,该类从 Fouladgar\EloquentBuilder\Support\Foundation\Contracts\Filter
抽象类扩展。此类要求您实现一个方法:apply
。根据需要,apply
方法可以添加查询的 where 约束。每个过滤器类应以 Filter
字尾。
例如,以下是一个过滤器定义的示例
<?php namespace App\EloquentFilters\User; use Fouladgar\EloquentBuilder\Support\Foundation\Contracts\Filter; use Illuminate\Database\Eloquent\Builder; class AgeMoreThanFilter extends Filter { /** * Apply the age condition to the query. */ public function apply(Builder $builder, mixed $value): Builder { return $builder->where('age', '>', $value); } }
提示:您还可以在过滤器中使用 本地作用域。因为,它们是查询构建器的实例。
通过 artisan 命令定义过滤器
如果您想轻松创建一个过滤器,可以使用 eloquent-builder:make
artisan 命令。此命令将接受至少两个参数,分别是 Model
和 Filter
。
php artisan eloquent-builder:make user age_more_than
您还可以同时创建多个过滤器。为了实现此目标,您应该将多个名称传递给 Filter
参数。
php artisan eloquent-builder:make user age_more_than gender
使用过滤器
您可以使用多种方法使用过滤器
<?php // Use by a model class name $users = EloquentBuilder::model(\App\Models\User::class)->filters(request()->all())->thenApply()->get(); // Use by existing query $query = \App\Models\User::where('is_active', true); $users = EloquentBuilder::model($query) ->filters(request()->all()) ->thenApply() ->where('city', 'london') ->get(); // Use by instance of a model and push filter $users = EloquentBuilder::model(new \App\Models\User()) ->filters(request()->filter) ->filter(['age_more_than' => '30']) ->filter(['gender' => 'female']) ->thenApply() ->get();
提示:建议将您的查询参数放在以下过滤器键中
user/search?filter[age_more_than]=25&filter[gender]=male
然后这样使用它们:request()->filter
。
使用预定义过滤器
此包提供了一些使用字符串约定的预定义过滤器,您可以在过滤器类中轻松使用它们。
提示:所有字符串约定中的值都将根据使用的过滤器进行验证。
日期过滤器
日期过滤是您可能使用的最常用的过滤器之一,您可以通过以下约定在过滤器中使用它们:between:date1,date2
、before:date
、before_or_equal:date
、after:date
、after_or_equal:date
、same:date
和 equals:date
。
示例
api/user/search?birth_date=before:2018-01-01 # These are similar between convention: api/user/search?birth_date=between:2018-01-01,2022-01-01 api/article/search?birth_date=2018-01-01,2022-01-01 api/article/search?birth_date[]=2018-01-01&birth_date[]=2022-01-01 # These are similar equals convention: api/user/search?birth_date=equals:2018-01-01 api/user/search?birth_date=same:2018-01-01 api/user/search?birth_date=2018-01-01
您只需要定义一个过滤器,并使用 Fouladgar\EloquentBuilder\Concerns\FiltersDatesTrait
特性。例如
<?php namespace App\EloquentFilters\User; use Fouladgar\EloquentBuilder\Concerns\FiltersDatesTrait; use Fouladgar\EloquentBuilder\Support\Foundation\Contracts\Filter; use Illuminate\Database\Eloquent\Builder; class BirthDateFilter extends Filter { use FiltersDatesTrait; public function apply(Builder $builder, mixed $value): Builder { return $this->filterDate($builder, $value, 'birth_date'); } }
数字过滤器
另一个预定义过滤器是数字过滤器,您可以在过滤器中使用它们。例如,对于价格过滤器、分数过滤器以及任何数字过滤器,这非常有用。您可以遵循以下数字约定
between:number1,number2
、gt:number
、gte:number
、lt:number
、lte:number
和 equals:number
。
示例
api/user/search?score=gte:500 # These are similar between convention: api/user/search?score=between:100,1010 api/article/search?score=100,1010 api/article/search?score[]=100&score[]=1010 # These are similar equals convention: api/user/search?score=equals:2222 api/user/search?score=2222
例如,创建一个 ScoreFilter
并使用 Fouladgar\EloquentBuilder\Concerns\FiltersNumbersTrait
特性,如下所示
<?php namespace App\EloquentFilters\User; use Fouladgar\EloquentBuilder\Concerns\FiltersNumbersTrait; use Fouladgar\EloquentBuilder\Support\Foundation\Contracts\Filter; use Illuminate\Database\Eloquent\Builder; class ScoreFilter extends Filter { use FiltersNumbersTrait; public function apply(Builder $builder, mixed $value): Builder { return $this->filterNumber($builder, $value, 'score'); } }
排序过滤器
您可能希望对查询过滤器进行排序。有一些用法可以使它变得
示例
api/user/search?sort_by[birth_date]=desc&sort_by[id]=asc api/user/search?sort_by[]=birth_date:desc&sort_by[]=id:asc # The default direction is `asc`: api/user/search?sort_by[]=birth_date
例如,创建一个SortByFilter
并使用Fouladgar\EloquentBuilder\Concerns\SortableTrait
特性。
<?php namespace App\EloquentBuilders\User; use Fouladgar\EloquentBuilder\Concerns\SortableTrait; use Fouladgar\EloquentBuilder\Support\Foundation\Contracts\Filter; use Illuminate\Database\Eloquent\Builder; class SortByFilter extends Filter { use SortableTrait; protected array $sortable = [ 'birth_date', 'score', ]; public function apply(Builder $builder, mixed $value): Builder { return $this->applySort($builder, $value); } }
提示:可排序的列(s)应由
$sortable
属性指定。
授权过滤器
过滤器类还包含一个authorize
方法。在这个方法中,您可以检查认证用户是否确实有权应用给定的过滤器。例如,您可以确定用户是否拥有高级账户,是否可以应用StatusFilter
以列出在线或离线的人
/** * Determine if the user is authorized to make this filter. */ public function authorize(): bool { if(auth()->user()->hasPremiumAccount()){ return true; } return false; }
默认情况下,您无需实现authorize
方法,过滤器将应用于您的查询构建器。如果authorize
方法返回false
,将自动返回带有403状态码的HTTP响应。
忽略null值上的过滤器
如果包含空或null值,将忽略过滤器参数。
假设您有一个类似的请求
//Get api/user/search?filter[name]&filter[gender]=null&filter[age_more_than]=''&filter[published_post]=true EloquentBuilder::model(User::class)->filters($request->filter)->thenApply(); // filters result will be: $filters = [ 'published_post' => true ];
只有"published_post"
过滤器将应用于您的查询。
按域/模块自定义
当您有一个具有自定义目录结构的laravel项目时,您可能需要在多个目录中拥有多个过滤器。为此,您可以使用setFilterNamespace()
方法并将所需的命名空间传递给它。
例如,假设您有一个实现基于域的结构的项目
.
├── app
├── bootstrap
├── config
├── database
├── Domains
│ ├── Store
│ │ ├── database
│ │ │ └── migrations
│ │ ├── src
│ │ ├── Filters // We put our Store domain filters here!
│ │ │ └── StoreFilter.php
│ │ ├── Entities
│ │ ├── Http
│ │ └── Controllers
│ │ ├── routes
│ │ └── Services
│ ├── User
│ │ ├── database
│ │ │ └── migrations
│ │ ├── src
│ │ ├── Filters // We put our User domain filters here!
│ │ │ └── UserFilter.php
│ │ ├── Entities
│ │ ├── Http
│ │ └── Controllers
│ │ ├── routes
│ │ └── Services
...
在上面的例子中,每个域都有自己的过滤器目录。因此,我们可以设置并使用自定义命名空间过滤器如下
$stores = EloquentBuilder::model(\Domains\Entities\Store::class) ->filters($request->all()) ->setFilterNamespace('Domains\\Store\\Filters') ->thenApply() ->get();
注意:在调用
setFilterNamespace()
时,将忽略默认命名空间和配置文件。
作为依赖注入使用
您可能需要在construct
或function
方法中将EloquentBuilder
用作DependencyInjection
。
假设您有一个UserController
,并且您想对用户应用一些过滤器来获取用户列表
<?php namespace App\Controllers; use App\Http\Resources\UserResource; use App\Models\User; use Fouladgar\EloquentBuilder\EloquentBuilder as Builder; use Fouladgar\EloquentBuilder\Exceptions\FilterException; use Illuminate\Http\Request; class UserController { public function index(Request $request, User $user, Builder $builder) { $users = $user->newQuery()->where('is_active', true); try { $builder->model($users) ->filters($request->filter) ->thenApply(); } catch (FilterException $filterException) { //... } return UserResource::collection($users->get()); } }
就是这样。
测试
composer test
贡献
有关详细信息,请参阅CONTRIBUTING。
安全
如果您发现任何安全相关的问题,请通过电子邮件fouladgar.dev@gmail.com而不是使用问题跟踪器。
许可证
Eloquent-Builder是在MIT许可证下发布的。有关详细信息,请参阅捆绑的LICENSE文件。
用❤️为您打造。