aldemeery/sieve

一种简单、简洁且优雅的方式来过滤Eloquent模型。

v2.0.4 2022-02-19 15:14 UTC

This package is auto-updated.

Last update: 2024-09-19 20:40:19 UTC


README

Build Status Total Downloads Latest Stable Version License

此包允许您根据查询字符串值过滤检索到的记录。

安装后,过滤从这样

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.8或更高版本。您可以通过Composer安装此包

composer require aldemeery/sieve

服务提供程序将自动注册。或者,您可以在您的 config/app.php 文件中手动添加服务提供程序

'providers' => [
    // ...
    Aldemeery\Sieve\FiltersServiceProvider::class,
];

使用

为模型启用过滤就像将 Aldemeery\Sieve\Concerns\Filterable 特性添加到您的模型(s)一样简单

use Aldemeery\Sieve\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 方法,您只需开始创建过滤器即可。

创建过滤器

过滤器类是扩展了 Aldemeery\Sieve\Filter 的类。

您可以手动创建一个过滤器类并将其放在您喜欢的位置,或者使用 make:filter artisan命令创建一个过滤器,该过滤器将被放置在 app/Http/Filters 目录中。

php artisan make:filter Product/ColorFilter

这将通常创建以下类

<?php

namespace App\Http\Filters\Product;

use Aldemeery\Sieve\Filter;
use Illuminate\Database\Eloquent\Builder;

class ColorFilter extends Filter
{
    /**
     * Values mappings.
     *
     * @var array
     */
    protected $mappings = [
        // Silence is golden...
    ];

    /**
     * Filter records based on a given value.
     *
     * @param \Illuminate\Database\Eloquent\Builder $builder Eloquent builder instance.
     * @param string $value The resolved value of the filtration key sent in the query string.
     *
     * @return void
     */
    public function filter(Builder $builder, $value)
    {
        //
    }
}

过滤器类中的 filter 方法是您应该放置您的过滤逻辑的地方。参数 $value 保存传递给查询字符串的特定过滤键的值。

/**
 * Filter records based on a given value.
 *
 * @param \Illuminate\Database\Eloquent\Builder $builder Eloquent builder instance.
 * @param string $value The resolved value of the filtration key sent in the query string.
 *
 * @return void
 */
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 数组中定义您的值映射,它们将在传递给 filter 方法之前自动解析,继续阅读...

示例:假设您想要按价格对产品列表进行排序,并且您不希望在URL中有: ..?price=asc..?price=desc,而是希望有类似: ..?price=lowest..?price=highest 的内容

为此,您有以下过滤器类

<?php

namespace App\Http\Filters\Product;

use Aldemeery\Sieve\Filter;
use Illuminate\Database\Eloquent\Builder;

class ColorFilter extends Filter
{

    /**
     * Values mappings.
     *
     * @var array
     */
    protected $mappings = [
        'lowest' => 'asc',
        'highest' => 'desc',
    ];

    /**
     * Filter records based on a given value.
     *
     * @param \Illuminate\Database\Eloquent\Builder $builder Eloquent builder instance.
     * @param string $value The resolved value of the filtration key sent in the query string.
     *
     * @return void
     */
    public function filter(Builder $builder, $value)
    {
        // URL: https://example.com/products?price=lowest

        // $value is automatically set to be 'asc' instead of 'lowest'
        if ($this->validateValue($value)) {
            $builder->orderBy('price', $value);
        }
    }

    /**
     * Determine if a given value is valid.
     *
     * @param string $value Value to validate.
     *
     * @return bool
     */
    private validateValue($value)
    {
        return in_array($value, ['asc', 'desc']);
    }
}

使用“单独”的过滤器

一旦您创建了您的过滤器并定义了您的过滤逻辑,现在是时候实际使用过滤器了,这可以通过三种方式完成

将过滤器数组传递给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 Aldemeery\Sieve\Concerns\Filterable;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use Filterable;

    /**
     * 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();
}

过滤器包

有时您可能有一个过滤器列表很大,使得您的模型看起来很杂乱,或者您可能想要将过滤器列表分组,以便在不同模型之间共享。这就是您想要有一个过滤器包的时候。

过滤器包是一个扩展了Aldemeery\Sieve\FilterBag的类,其中包含一组始终一起应用的过滤器。

您可以手动创建自己的过滤器包类并将其放置在您喜欢的位置,或者使用make:filter-bag命令创建一个过滤器包类,并将其放置在app\Http\Filters目录中。

php artisan make:filter-bag Product/ProductFilters

这将通常创建以下类

<?php

namespace App\Http\Filters\Product;

use Aldemeery\Sieve\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 Aldemeery\Sieve\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();
}

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件