kodilab/laravel-filters

此包已被弃用且不再维护。未建议替代包。

Laravel 过滤系统

v1.0.0 2019-07-04 16:14 UTC

This package is auto-updated.

Last update: 2020-09-06 11:10:15 UTC


README

Laravel-filters 是一个用于处理 Eloquent 模型过滤和集合过滤的 Laravel 包。该项目基于 Laracasts 视频教程

使用 laravel-filters,您可以通过简单、清晰且易于维护的方式应用过滤器。

默认情况下,过滤器直接与数据结构工作。当您正在过滤一个 Collection 时,过滤基于集合的 array 结构。如果您正在过滤一个 Eloquent model,则过滤基于该 model 的数据库结构。

但是,您可以覆盖特定的过滤器或创建一个新的过滤器来定义过滤器的工作方式。我们将在下一节中介绍。现在让我们先使用默认过滤器。

例如,这是在 Controller 中调用过滤器的样子


// GET /cars?color=red&order_desc=created_at


$cars = Car::where(...)->filters(QueryFilter::class, $request->all())->get();

我们调用过滤器并传递请求查询参数数组(在这种情况下,['color' => 'red', 'order_desc' => 'created_at'])。因此,laravel-filters 将应用一个名为 color 的过滤器,其值为 red,以及“过滤器”(它不是一个真正的过滤器,我们稍后会解释)order_desc,其值为 created_at

因为我们既没有覆盖也没有创建任何自定义过滤器,所以过滤器将使用前面解释的默认行为应用。

因此,在这种情况下,我们正在请求数据库中 color 列包含 red 值的汽车 Eloquent model,并按 created_at 列降序排序。如您所见,我们通过没有额外代码的方式过滤了结果。简单且清晰。

如前所述,这仅是默认过滤行为。您可以通过覆盖过滤器或创建新的过滤器来获得所需的过滤行为。但让我们一步一步来。

1 - 入门

只需使用 composer 安装此包

composer require kodilab/laravel-filters ^1.0.0

2 - Eloquent 模型过滤器

如简介中所述,您能够过滤 Eloquent modelsCollections。为了使 model 准备就绪以便可过滤,添加 Filterable 特性

use Illuminate\Database\Eloquent\Model;
use Kodilab\LaravelFilters\Traits\Filterable;

class Car extends Model
{
    use Filterable;
    
    ...
}

这就足够了。现在您可以使用过滤器,就像使用该 modelscope 一样

Car::filters(QueryFilters::class, ['filterA' => 'valueFilterA'])->get();

2.1 - filters() 范围

Filterable 特性只为模型添加了一个范围,即 filters(),其签名如下(作为范围签名)

filters(string $filter_class, array $input = [], string $prefix = '')

  • filter_class:过滤器类。您可以使用默认的一个(QueryFilters)或扩展它以创建新的过滤器或覆盖它们(这一步稍后会解释)。目前我们使用 QueryFilters:class
  • input:一个关联数组,包含 [filter => value] 项。
  • 前缀:有时,尤其是当你直接使用输入的 Request 数组($request->all())时,你不想使用该数组的所有项目。你可以使用前缀来标识你想要使用的项目。例如,如果你定义 qf- 作为前缀,则 qf-name 将从数组中作为过滤器(并通过 name 列进行过滤)提取,而 color 将被忽略,因为它不以 qf- 开头。

2.2 - 默认过滤器行为解释

当过滤过程开始时,输入数组中的每个项目都会经过相同的处理。让我们假设 $input = ['color' => 'red'] 是输入数组。 laravel-filters 将在 $filter_class 类上查找名为 color 的自定义方法。如果存在,则应用该方法所做的操作(这就是你如何定义自定义过滤器和覆盖过滤器的。我们将在下一节中详细说明)。如果没有自定义方法存在,则触发默认行为:它会尝试在模型的数据库表上查找名为 color 的列。如果存在,则按该列进行过滤。否则,它将简单地忽略该过滤器。

2.3 - 使用运算符处理默认行为

到目前为止,默认行为过滤器是按等式过滤。你可以进一步操作,添加一个 operator 来改变比较方式。想象你定义了这个输入: $input = ['year' => '2000', 'year-op' => 'gte']。当一个过滤器有 -op 后缀时,它有特殊的意义:是一个过滤器运算符附加组件。它将修改过滤的默认行为。在这种情况下,由于我们定义了运算符为 gte,它将按 year 列的值大于等于 2000 进行过滤(而不是使用 =)。

这里你可以看到可用的默认运算符及其含义

"eq" => '='
"neq"=> '<>'
"gt" => '>'
"gte" => '>='
"lt" => '<'
'lte' => '<='

让我们用一个更复杂的例子来说明在控制器方法中使用请求数组

// GET /cars?color=red&color-op=neq&year=2000&year-op=gt

public function index(Request $request)
{
    $cars = Cars::filters(QueryFilters:class, $request->all());
}

在这种情况下,$cars 将包含所有颜色不是红色(使用 neq 运算符)且年份大于 2000 的汽车。

排序方法

排序不被视为“过滤器”,但 laravel-filters 提供了一些帮助排序的实用方法。你可以使用“过滤器” order_descorder_asc 来指示如何排序结果。就像我们在过滤器中做的那样,你只需要指示用于排序的列名。

以上一个例子为例,你可以这样定义排序

// GET /cars?color=red&color-op=neq&year=2000&year-op=gt&order_desc=color

public function index(Request $request)
{
    $cars = Cars::filters(QueryFilters:class, $request->all());
}

我们将得到颜色不是红色且年份大于 2000 的汽车,并按 color 列排序。

3 - 集合过滤器

集合过滤器的功能与集合过滤器的工作方式相同。为了在集合中使用过滤器,你有两种选择

3.1 - 手动实例化过滤器

这是一种最保守的方法,因为你不需要创建一个扩展的 Collection。然而,你必须每次想要过滤时都自己实例化过滤器。

这是一个在 Controller 方法中使用过滤器的例子

public function index(Request $request)
{
    //Cars contains a collection
    $data = new Collection($data);
    
    //Instance the filters
    $filters = new CollectionFilters();
    
    //Then apply the filters. Apply() will return the filtered collection
    $cars = $filters->apply($data, $request->all());
}

在这种情况下,我们不是使用 QueryFilters 过滤类,而是使用 CollectionFilters。这很重要,当你与 Eloquent 模型 一起工作时,你必须使用 QueryFilters 或其扩展类。当你与 Collection 一起工作时,必须使用 CollectionFilters 或其扩展类。

3.2 - 扩展 Collection

这种方法稍微复杂一些,但它将让您以与使用 Eloquent 模型 相似的方式使用过滤器。首先,您应该创建一个继承自 \Illuminate\Support\Collection 的 Collection 类,并添加一个名为 filters() 的方法。记住,当您想过滤集合时,请使用这个新类而不是原始的 Collection

class Collection extends \Illuminate\Support\Collection
{
    public function filters(string $filter_class, array $input = [], string $prefix = '')
    {
        /** @var CollectionFilters $filters */
        $filters = new $filter_class();

        return $filters->apply($this, $input, $prefix);
    }
}

然后,您可以使用与模型过滤相似的方式使用 Collection 过滤器

public function index(Request $request)
{
    //Cars contains a collection. Remember use the extended Collection you created before
    $data = new Collection($data);
    
    $cars = $data->filters(CollectionFilters::class, $request->all());
}

同样,我们在这里使用 CollectionFilters 而不是 QueryFilters,因为我们正在处理 Collections

4 - 自定义过滤器

到目前为止,我们描述了默认行为。无需编写代码。所有操作都在后台进行。然而,有时您可能想要创建更多过滤器或覆盖现有的过滤器。这非常简单,只需扩展 QueryFiltersCollectionFilters 类并创建自己的方法。

通常,您将为每个模型创建一个扩展的 QueryFilters 类,因为每个模型都将有自己的自定义过滤器和要求。您将为您的项目中的每个集合创建一个扩展的 CollectionFilters

让我们为 Car 模型类创建一个扩展的 QueryFilters 类

class CarFilters extends QueryFilters
{
    /*
     * We are overridin the filter "color" in order to filter cars with an specific color which wheels are the same color
     */
    protected function color($value)
    {
        //The idea here is adding statements to the $this->results (QueryBuilder containing the results)
        // as we are extending a QueryFilters class
        
        // If you are extending from CollectionFilters, $this->results contains the Collection.
        
        //We change the behaviour, now filter color will filter by the car color AND the wheels color. 
        //The operator for this filter will be ignored
        
        $this->results->where('color_wheels', $value)->where('color', $value);
        
        //The new results MUST be returned
        
        return $this->results;
    }
}

现在,如果我们使用之前的相同请求

// GET /cars?color=red&color-op=neq&year=2000&year-op=gt&order_desc=color

public function index(Request $request)
{
    $cars = Cars::filters(CarFilter:class, $request->all());
}

如您所见,我们更改了 filters() 的参数 QueryFilters:class,以使用新的扩展类 CarFilters::class

在这种情况下,我们通过使用我们自定义的过滤器 color 获取到红色汽车,其轮子也是红色的。

您可以通过 $this->getFilterOperator(string $filter_name, $default = null) 轻松访问自定义过滤器中的操作符,它返回用于直接在 where 语句中使用(=, !=, <>, <, <=, >, >=)的比较符号(如果存在)。