xbhub / laravel-filter
一种简单、清晰且优雅的过滤 Eloquent 模型的方法。
Requires
- php: ^7.4|^8.0
README
此包允许您根据查询字符串值过滤检索的记录。
一旦安装,过滤将缩小为
public function index(Request $request)
{
$query = Product::query();
if ($request->has('color')) {
$query->where('color', $request->get('color'));
}
if ($request->has('condition')) {
$query->where('condition', $request->get('condition'));
}
if ($request->has('price')) {
$direction = $request->get('price') === 'highest' ? 'desc' : 'asc';
$query->orderBy('price', $direction);
}
return $query->get();
}
到
public function index(Request $request)
{
return Product::filter($request)->get();
}
安装
此包可用于 Laravel 5.3 或更高版本。您可以通过 composer 安装此包
composer require xbhub/filter
服务提供程序将自动注册。或者您可以在您的 config/app.php
文件中手动添加服务提供程序
'providers' => [
// ...
Xbhub\Filter\FiltersServiceProvider::class,
];
用法
为模型启用过滤与将 Xbhub\Filter\Concerns\Filterable
特性添加到您的模型一样简单
use Xbhub\Filter\Concerns\Filterable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use Filterable;
// ...
}
Filterable
特性会给您的模型添加一个 filter
局部作用域,它接受一个 Illuminate\Htpp\Request
实例,因此它可以用于过滤,如下所示
public function index(Request $request)
{
return Product::filter($request)->get();
}
一旦您将特性添加到您的模型中并使用了 filter
方法,您只需开始创建过滤器。
创建过滤器
过滤器类是一个扩展 Xbhub\Filter\Filter
的类。
您可以手动创建一个过滤器类并将其放在您喜欢的位置,或者使用 make:filter
artisan 命令创建一个过滤器,该过滤器将被放置在 app/Http/Filters
目录中。
php artisan make:filter Product/ColorFilter
这将通常创建以下类
<?php
namespace App\Http\Filters\Product;
use Xbhub\Filter\Filter;
use Illuminate\Database\Eloquent\Builder;
class ColorFilter extends Filter
{
/**
* Filter values mappings.
*
* @var array
*/
protected $mappings = [
//
];
/**
* Filter records.
*
* @param Builder $builder
* @param mixed $value
*
* @return Builder
*/
public function filter(Builder $builder, $value)
{
//
}
}
过滤器类中的 filter
方法是您应该放置您的过滤逻辑的地方。$value
参数持有传递给此特定过滤器键的查询字符串值。
/**
* Filter records.
*
* @param Builder $builder
* @param mixed $value
*
* @return Builder
*/
public function filter(Builder $builder, $value)
{
// Assuming the URL is https://example.com/products?color=red
// $value here is equal to 'red'
return $builder->where('color', $value);
}
注意:上述示例中的键 'color' 是在您实际 使用过滤器 时定义的
因为您有一个 Illuminate\Database\Eloquent\Builder
的实例,所以您拥有了它的全部功能,这意味着您可以做所有不同种类的事情
- 排序
public function filter(Builder $builder, $value) { return $builder->orderBy('price', $value); }
- 根据关系进行过滤
public function filter(Builder $builder, $value) { return $builder->whereHas('category', function($query) use ($value){ return $query->where('name', $value); }); }
映射
有时您可能想为您的查询字符串键使用更有意义的值,而这些值可能与您实际需要的过滤值不同,这就是 $mappings
数组发挥作用的地方。
您可以在 $mappings
数组中定义您的值映射,然后使用过滤器类中的 resolveValue
方法来解决这些值。
resolveValue
方法从 $mappings
数组中解析值,如果找不到值则返回 null
。
注意
null
,因为它有时会在过滤或排序记录时导致意外的行为。
示例:假设您想要按价格对产品列表进行排序,并且您不希望在 URL 中有 ..?price=asc
或 ..?price=desc
,而是希望有 ..?price=lowest
或 ..?price=highest
为了实现这一点,您有以下过滤器类
<?php
namespace App\Http\Filters\Product;
use Xbhub\Filter\Filter;
use Illuminate\Database\Eloquent\Builder;
class ColorFilter extends Filter
{
/**
* Filter values mappings.
*
* @var array
*/
protected $mappings = [
'lowest' => 'asc',
'highest' => 'desc',
];
/**
* Filter records.
*
* @param Builder $builder
* @param mixed $value
*
* @return Builder
*/
public function filter(Builder $builder, $value)
{
// Assume $value is 'lowest'
$value = $this->resolveValue($value);
// $value now is 'asc'
// URL doesn't have neither 'asc' nor 'desc'
if ($value === null) {
return $builder // Don't do anything
}
return $builder->orderBy('price', $value);
}
}
使用“单个”过滤器
一旦您创建了您的过滤器和定义了您的过滤逻辑,现在是时候实际使用过滤器了,这可以通过以下三种方式完成
将过滤器数组传递给filter
当您想要对一个单独的查询应用过滤器时使用此方法
public function index(Request $request)
{
return Product::filter($request,[
// "color" here is the key to be used in the query string
// e.g. https://example.com/products?color=red
"color" => \App\Http\Filters\Product\ColorFilter::class,
])->get();
}
在上面的例子中,过滤器将只在这个查询的查询字符串中查找键color的值,并将其传递给您在过滤器类中放置过滤逻辑的filter
方法内部。
定义模型过滤器
Filterable
特质提供了一个filters
方法,该方法应该返回一个包含“键”和“过滤器”的数组,每次调用filter
方法时都应用于模型。
<?php
namespace App;
use Xbhub\Filter\Concerns\Filterable;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use Filterable;
protected $fieldSearchable = [
'name' => 'like',
'id' => '=',
'email' => 'like'
];
/**
* List of individual filters to be used by the model.
*
* @return array
*/
protected function filters()
{
return [
'color' => \App\Http\Filters\Product\ColorFilter::class,
];
}
}
现在每次您在模型上调用filter
方法时,都会自动应用ColorFilter
而无需传递任何外部参数。
public function index(Request $request)
{
// The ColorFilter is automatically applied.
return Product::filter($request)->get();
}
过滤器包
有时您可能有一个大型过滤器列表,这会让您的模型看起来很杂乱,或者您可能想将过滤器列表分组,以便在不同的模型之间共享。这就是您需要过滤器包的时候。
过滤器包是一个扩展自Xbhub\Filter\FilterBag
的类,它包含一组总是一起应用的过滤器。
您可以选择手动创建过滤器包类并将其放置在您喜欢的位置,或者使用make:filter-bag
命令来创建一个放在app\Http\Filters
目录内的过滤器包类。
php artisan make:filter-bag Product/ProductFilters
这将通常创建以下类
<?php
namespace App\Http\Filters\Product;
use Xbhub\Filter\FilterBag;
class ProductFilters extends FilterBag
{
/**
* Filters to be applied.
*
* @var array
*/
protected static $filters = [
//
];
}
同样,将您的“键”和“过滤器”对放入$filters
数组中
/**
* Filters to be applied.
*
* @var array
*/
protected static $filters = [
'color' => \App\Http\Filters\Product\ColorFilter::class,
'condition' => \App\Http\Filters\Product\UsedFilter::class,
'q' => \App\Http\Filters\Product\NameFilter::class,
't' => \App\Http\Filters\TrashedFilter::class,
];
完成这些后,您只需要覆盖模型中的Filterable
特质的filterBag
方法,以返回应该使用的过滤器包。
<?php
namespace App;
use Xbhub\Filter\Concerns\Filterable;
use App\Http\Filters\Product\ProductFilters;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use Filterable;
/**
* Filter bag used by the model.
*
* @return string
*/
protected function filterBags()
{
return [
ProductFilters::class,
];
}
}
现在每次在模型上调用filter
方法时,都会应用ProductFilters
中的所有过滤器。
public function index(Request $request)
{
// The filters from ProductFilters are automatically applied.
return Product::filter($request)->get();
}
默认过滤器
/users?search=name:John;email:john@gmail.com
/users?search=age:17;email:john@gmail.com&searchJoin=and
/users?filter=id;name
/users?filter=id;name&orderBy=id&sortedBy=desc
/users?orderBy=posts|title&sortedBy=desc
/users?with=groups
/users?withCount=groups
灵感来源
许可证
MIT许可证(MIT)。请参阅许可证文件以获取更多信息。