thejano/laravel-filterable

为 Laravel 模型添加过滤功能

1.4.0 2024-03-20 13:13 UTC

This package is auto-updated.

Last update: 2024-09-08 23:08:57 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

此软件包为 Laravel 模型添加过滤功能。它基于 Filterable 和 Query Filter 类。该软件包将提供生成 Filterable 和 Query Filter 类的命令。默认情况下,它将添加一些默认过滤功能,例如排序、获取两个日期之间的数据等。

想象一下,您有一个包含以下参数的 URL

/posts?slug=the-new-web&published=true&category=web-development&tags[]=web&tags[]=laravel&tags[]=flutter

Laravel 请求所有方法 request()->all() 将返回类似这样的结果

[
    "slug"        => "the-new-web",
    "published"   => "true",
    "category"    => "web-development",
    "tags"        => [ "web", "laravel", "flutter"],
]

通常,您应该逐个执行逻辑以执行过滤

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;

class PostController extends Controller
{
    public function index(Request $request)
    {
        $query = Post::query();

        if ($request->has('title'))
        {
            $query->where('title', 'LIKE', '%' . $request->input('title') . '%');
        }
       
       if ($request->has('published'))
        {
            $query->where('published', (bool) $request->input('published'));
        }

        if ($request->has('category')){
            $query->whereHas('category', function ($query) use ($request)
            {
                return $query->where('category_slug', $request->input('category'));
            });
        }
        
        if ($request->has('tags')){
            $query->whereHas('tag', function ($query) use ($request)
            {
                return $query->where('tag_slug', $request->input('tags'));
            });
        }
        $posts = $query->get();
        return view('posts',compact('posts'));
    }

}

对于简单的查询,这很好,但当您有很多过滤器时,您无法控制它们,并且它们都不能重用。

使用此软件包,您只需要在返回记录之前将 filterable() 范围方法传递给您的模型,如下面的示例所示

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Post;

class PostController extends Controller
{
    public function index(Request $request)
    {
        $posts = Post::filterable()->get();
        return view('posts',compact('posts'));
    }

}

要求

该软件包需要

  • PHP 8.1 或更高版本
  • Laravel 10.x 或更高版本

安装

您可以通过 composer 安装此软件包

composer require thejano/laravel-filterable

您可以使用以下命令发布配置文件

php artisan vendor:publish --tag="filterable-config"

用法

要添加魔法,您只需将 hasFilterableTrait 添加到您的模型中。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use TheJano\LaravelFilterable\Traits\HasFilterableTrait;

class Post extends Model
{
    use HasFactory;
    use HasFilterableTrait;
}

然后您需要创建一个 FilterableClass 和一些 Query Filters 以定义您的规则。


为了消除创建类的痛苦,我已经添加了一些命令。

  • 要创建一个 Filterable 类,您需要运行此命令
php artisan make:filterable PostsFilterable

它将在 \App\Filters\Filterable\PostsFilterable 下生成一个类,并包含

<?php

namespace App\Filters\Filterable;

use TheJano\LaravelFilterable\Abstracts\FilterableAbstract;
use TheJano\LaravelFilterable\Interfaces\FilterableInterface;

class PostsFilterable extends FilterableAbstract implements FilterableInterface
{
    /**
     * It contains list of Query Filters
     *
     * @var Array
     */
    protected array $filters = [];
}

  • 现在让我们创建一个 Query Filter 类
php artisan make:query-filter PublishedQueryFilter

它将在 \App\Filters\QueryFilter\PublishedQueryFilter 下生成一个类,并包含

<?php

namespace App\Filters\QueryFilter;

use Illuminate\Database\Eloquent\Builder;
use TheJano\LaravelFilterable\Abstracts\QueryFilterAbstract;
use TheJano\LaravelFilterable\Interfaces\QueryFilterInterface;

class PublishedQueryFilter extends QueryFilterAbstract implements QueryFilterInterface
{
    /**
     * Can be used to map the values.
     * It can be returned through resolveValue method
     *
     * @var Array
    */
    protected array $mapValues = [];

    /**
     * Handle The Query Filter
     *
     *
     * @param Builder $builder Query Builder
     * @param string $value
     * @return Builder
    **/
    public function handle(Builder $builder, $value): Builder
    {
        return $builder;
    }
}

您将在每个 Query Filter 中单独执行每个列的逻辑。让我们在这里实现逻辑

public function handle(Builder $builder, $value): Builder
{
    return $builder->where('published', $value);
}

返回值是一个字符串,它不返回任何数据。因此,我们应该映射值。

类中有一个 $mapValues 属性。

protected array $mapValues = [
    'true' => true,
    'false' => false,
];

最后,我们应该通过 resolveValue() 方法解析映射的值。

protected array $mapValues = [
    'true' => true,
    'false' => false,
];

public function handle(Builder $builder, $value): Builder
{
    $value = $this->resolveValue($value);
    
    // return Builder if the value is null     
    if (is_null($value)) {
        return $builder;
    }

    return $builder->where('published', $value);
}

为了使 Query Filter 生效,我们应该将其附加到 PostsFilterable 类的 $columns 属性。

public array $filters = [
    'published' => 'App\\Filters\\QueryFilter\\PublishedQueryFilter',
];

  • 当您直接创建 Query Filter 时,您可以将 Filterable 类作为参数传递以自动插入到 $filters 属性中。
php artisan make:query-filter PublishedQueryFilter --filterable=PostsFilterable

现在您的 PostsFilterable 类应该包含以下内容

<?php

namespace App\Filters\Filterable;

use TheJano\LaravelFilterable\Abstracts\FilterableAbstract;
use TheJano\LaravelFilterable\Interfaces\FilterableInterface;

class PostsFilterable extends FilterableAbstract implements FilterableInterface
{
    /**
     * It contains list of Query Filters
     *
     * @var Array
     */
    public array $filters = [
        'published' => 'App\\Filters\\QueryFilter\\PublishedQueryFilter',
    ];
}

最后一步是启用 PostsFilterable 到您的模型。

有三种方法可以启用它

  1. 什么都不做 :) 这将启用所有默认 Query Filters 到您的模型。

  2. 将 Filterable 类传递到模型的 filterableClass() 方法。

<?php

namespace App\Models;

use App\Filters\Filterable\PostsFilterable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use TheJano\LaravelFilterable\Traits\HasFilterableTrait;

class Post extends Model
{
    use HasFactory;
    use HasFilterableTrait;

    /**
     * Enable the filterable class to the model
     *
     * @return void
     */
    public function filterableClass()
    {
        return PostsFilterable::class;
    }
}

  1. 将作为参数传递到模型的 fliterable() 范围。该范围接受 3 个参数
public function scopeFilterable(Builder $builder, $request = null, $filterableClass = null, $filters = []): Builder

如果您将 Filterable 类作为第一个参数传递,底层软件包将为您处理它,并忽略 $request 变量,并使用 request() 助手函数。让我们看看下面的代码。

<?php

namespace App\Http\Controllers;

use App\Filters\Filterable\PostFilterable;
use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index(Request $request)
    {
        $posts = Post::filterable(PostFilterable::class)->get();
        return view('posts', compact('posts'));
    }
}

您还可以通过 $filters 参数传递一些额外的 Query Filters,例如

<?php

namespace App\Http\Controllers;

use App\Filters\Filterable\PostFilterable;
use App\Filters\QueryFilter\TitleQueryFilter;
use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index(Request $request)
    {
        $posts = Post::filterable(PostFilterable::class,[
            'title' => TitleQueryFilter::class
        ])->get();
        return view('posts', compact('posts'));
    }
}

您不仅可以传递 Request,还可以传递参数数组进行过滤。

默认 Query Filters

最后但同样重要的是,默认情况下,该包为每个可过滤类提供一些查询过滤器。配置文件包含可用的查询过滤器,包括:


  1. date 查询过滤器,它返回两个日期之间的记录(从和到)。默认情况下,它使用 created_at 字段。
/posts?date[from]=2022-06-01&date[to]=2022-07-01

或者,您可以传递一个自定义字段。分隔符是 BY

/posts?date[fromBYupdated_at]=2022-06-01&date[toBYupdated_at]=2022-07-01

  1. order 查询过滤器,它按 ASC 或 DESC 对记录进行排序。默认情况下,它使用 created_at 字段。
/posts?order=asc

或者,您可以传递一个自定义字段。

/posts?order[id]=asc

测试

composer test

变更日志

有关最近更改的更多信息,请参阅变更日志

贡献

有关详细信息,请参阅贡献指南

安全漏洞

请查看我们关于如何报告安全漏洞的安全策略

鸣谢

许可

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