Eloquent 过滤器

建议编辑

介绍

Eloquent 过滤器是用于在 Laravel 中创建复杂查询的强大工具。 它们允许您轻松管理和自定义搜索条件。 您可以使用 Eloquent 过滤器根据属性、品牌和其他条件过滤产品目录。

要创建新的 Eloquent 过滤器,可以使用 php artisan orchid:filter 命令,后跟所需的过滤器名称。 此命令将在 app/Orchid/Filters 文件夹中生成一个新的过滤器类。

以下是创建 EmailFilter 的示例:

php artisan orchid:filter EmailFilter

生成的过滤器类如下所示:

namespace App\Http\Filters;

use Orchid\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;

class EmailFilter extends Filter
{
    /**
     * 匹配参数的数组。
     *
     * @var array
     */
    public $parameters = ['email'];

    /**
     * 如果请求参数满足,则应用过滤器。
     * 
     * @param Builder $builder
     *
     * @return Builder
     */
    public function run(Builder $builder): Builder
    {
        return $builder->where('email', $this->request->get('email'));
    }

    /**
     * 获取显示字段。
     *
     * @return Field[]
     */
    public function display(): array
    {
        return [
            Input::make('email')
                ->type('text')
                ->value($this->request->get('email'))
                ->placeholder('搜索...')
                ->title('搜索')
        ];
    }
}

要在自己的模型中使用过滤器, 需要连接 Orchid\Filters\Filterable trait,并将类数组传递给 filters 函数:

use App\Model;

Model::filters([EmailFilter::class])->simplePaginate();

为不同模型使用过滤器

过滤器的一个巨大优势是可以为不同模型重用相同的过滤器类。 这允许您定义一次过滤器并将其应用于多个模型,减少代码重复。 只需在应用过滤器到模型时指定过滤器类:

$filters = [
    EmailFilter::class,
];

User::filters($filters)
    ->simplePaginate();

Customer::filters($filters)
    ->simplePaginate();

始终运行过滤器

默认情况下,只有在指定相应参数时才会应用过滤器。 但是,如果希望在每个请求上运行过滤器,可以将过滤器类中的 $parameters 属性留为空数组。 这样,过滤器将应用于所有查询。例如:

public $parameters = [];

此外,如果 display 方法返回的数组为空,过滤器将不会出现在界面中,但仍将在查询中保持功能。这允许灵活配置,使某些过滤器在后台静默运行。

参数模式

过滤器提供了使用便捷语法定义参数模式的能力。 这允许您创建自定义模式并根据特定模式执行高级过滤。例如:

/**
 * 匹配参数的数组。
 *
 * @var null|array
 */
public $parameters = [
    'pattern.*',
];

在上述示例中,过滤器将匹配任何符合 pattern.* 模式的参数。这允许您在过滤器中处理广泛的动态参数。

自定义过滤器显示值

默认情况下,过滤器值以 name:value 格式显示,这对于广泛的过滤器非常有效。然而,当处理 ID 或系统名称时,您可能希望显示更友好的值。

为此,您可以定义一个返回字符串的 value 方法。例如:

public function value(): string
{
    return '您的自定义值';
}

如果应用了过滤器,此字符串将在用户界面中显示。

这种方法在使用依赖于 ID 或系统特定名称的过滤器时特别有用,允许您为最终用户提供更有意义或描述性的输出。

选择

“选择” 层提供了一种方便的方法来分组和组织过滤器,以便将它们显示和应用于模型。 此层充当用户界面和模型之间的中介,简化了过滤器管理过程。

要创建 “选择” 层,可以使用以下命令:

php artisan orchid:selection MailingSelection

此命令将在 App\Orchid\Layouts 目录中生成一个名为 MailingSelection 的新 PHP 文件。 在此类中,您将找到一个名为 filters() 的方法。 此方法是您应列出所有需要显示和应用的过滤器的地方。

例如,假设您想显示和应用两个过滤器:一个电子邮件过滤器和一个创建过滤器。 您的 MailingSelection 类将如下所示:

namespace App\Orchid\Layouts;

use App\Orchid\Filters\EmailFilter;
use App\Orchid\Filters\CreatedFilter;
use Orchid\Screen\Layouts\Selection;

class MailingSelection extends Selection
{
    /**
     * @return Filter[]
     */
    public function filters(): array
    {
        return [
          EmailFilter::class,
          CreatedFilter::class
        ];
    }
}

定义 MailingSelection 类中的过滤器后,可以通过使用 filters() 方法将它们应用于模型。例如:

Model::filters(MailingSelection::class)->simplePaginate();

通过在模型上调用 filters() 方法并传递 MailingSelection::class 作为参数,可以将 MailingSelection 类中定义的过滤器应用于模型。

在屏幕上显示

“选择” 层还可以用于在屏幕上显示过滤器。 在屏幕的 layout() 方法中,可以包含 MailingSelection 类以在屏幕上显示过滤器。 例如:

use App\Orchid\Layouts\MailingSelection;

public function layout(): array
{
    return [
        MailingSelection::class,
    ];
}

请注意,具有空字段的过滤器将不会被渲染,以确保界面简洁和用户友好。

使用模板自定义

选择层的外观可以有所不同,例如显示为下拉列表(默认)或表单。您可以使用 template 属性定义此项。例如:

class MailingSelection extends Selection
{
    /**
     * 渲染选择的模板。
     * 
     * 您可以使用任何 Blade 模板。默认:
     * - `self::TEMPLATE_LINE` 用于线性布局
     * - `self::TEMPLATE_DROP_DOWN` 用于下拉菜单
     */
    public $template = self::TEMPLATE_DROP_DOWN;
    
    /**
     * @return Filter[]
     */
    public function filters(): array
    {
        return [
            //...
        ];
    }
}

此外,您可以在此属性中定义自己的 Blade 模板。

处理 HTTP 参数

为了根据用户提供的 HTTP 参数自动过滤和排序应用程序的数据,包提供了一套强大而灵活的工具。 有效利用这些工具的关键是确保您的模型包含 Filterable trait 并实现可接受的过滤和排序参数的白名单。

要使您的 App\Models\Post 模型可过滤,请按照以下步骤操作:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Orchid\Filters\Filterable;
use Orchid\Filters\Types\Like;
use Orchid\Filters\Types\Where;
use Orchid\Filters\Types\WhereDate;
use Orchid\Filters\Types\WhereMaxMin;
use Orchid\Filters\Types\WhereDateStartEnd;

class Post extends Model
{
    use Filterable;
}

过滤

如前所述,任何过滤操作只能在允许的过滤器列表上执行,该列表使用 allowedFilters 属性作为键值对指定。键表示列的名称,值是处理类。以下是一个示例:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Orchid\Filters\Filterable;
use Orchid\Filters\Types\Like;
use Orchid\Filters\Types\Where;
use Orchid\Filters\Types\WhereDate;
use Orchid\Filters\Types\WhereMaxMin;
use Orchid\Filters\Types\WhereDateStartEnd;

class Post extends Model
{
    use Filterable;

    /**
     * 可以在 URL 中使用过滤器的属性。
     *
     * @var array
     */
    protected $allowedFilters = [
        'id'            => Where::class,
        'user_id'       => WhereIn::class,
        'rating'        => WhereMaxMin::class,
        'content'       => Like::class,
        'publish_at'    => WhereDate::class,
        'created_at'    => WhereDateStartEnd::class,
        'deleted_at'    => WhereDateStartEnd::class,
    ];
}

一旦指定了列表,使用自动过滤就很简单。只需调用 filters() 方法,例如:

Post::filters()->paginate();

通过利用这种方法,您可以轻松地将过滤器应用于查询并分页结果。

此代码将自动应用用户 HTTP 请求中包含的任何过滤器或排序规则。

查询示例

为了有效使用此功能,重要的是要对 HTTP 参数如何转换为数据库查询有一个清晰的理解。例如:

http://example.com/demo?filter[id]=1
$model->where('id', '=', 1)

此查询将在模型的 id 列上应用 where 子句,过滤掉不匹配用户提供值的记录。

http://example.com/demo?filter[name]=A
$model->where('name', 'like', '%A%')

此查询将在模型的 name 列上应用 like 子句,搜索名称中包含字母 “A” 的任何记录。

http://example.com/demo?filter[id]=1,2,3,4,5
$model->whereIn('id', [1,2,3,4,5]);

此查询将在模型的 id 列上应用 wherein 子句,过滤匹配指定 ID 的任何记录。

http://example.com/demo?filter[id][min]=1&filter[id][max]=5
$model->whereBetween('id', [1,5]);

此查询将在模型的 id 列上应用 wherebetween 子句,过滤 ID 在 1 和 5 之间的记录。

http://example.com/demo?filter[id][]=1&filter[id][]=2&filter[id][]=3
$model->whereIn('id', [1,2,3]);

此查询将在模型的 id 列上应用 whereIn 子句,过滤 ID 为指定值之一的记录。

http://example.com/demo?filter[rating][min]=1&filter[rating][max]=5
$model->where('rating', '>=', 1)
    ->where('rating', '<=', 5);

此查询将在模型的 rating 列上应用两个单独的 where 子句,过滤评分在 1 和 5 之间的记录。

http://example.com/demo?filter[rating][min]=1
$model->where('rating', '>=', 1);

此查询将在模型的 rating 列上应用单个 where 子句,过滤评分大于或等于 1 的记录。

http://example.com/demo?filter[publish_at]=2023-02-02
$model->where('publish_at', '2023-02-02')

此查询将在模型的 publish_at 列上应用单个 where 子句,过滤 publish_at 日期恰好等于 2023 年 2 月 2 日的记录。

http://example.com/demo?filter[created_at][start]=2023-01-01&filter[created_at][end]=2023-02-02
$model->whereDate('created_at', '>=', '2023-01-01')
    ->whereDate('created_at', '<=', '2023-02-02');

此查询将在模型的 created_at 列上应用两个单独的 whereDate 子句,过滤 created_at 日期在指定范围内的记录。

http://example.com/demo?filter[created_at][start]=2023-01-01
$model->whereDate('created_at', '>=', '2023-01-01')

此查询将在模型的 created_at 列上应用单个 whereDate 子句,过滤 created_at 日期大于或等于 2023 年 1 月 1 日的记录。

HTTP 过滤器或排序没有单独的显示模板。您可以在表头中看到此用法的示例。

排序

要启用排序功能,您需要在 allowedSorts 属性中指定列的列表。此列表表示数据库表中可用于排序的列。以下是一个示例:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Orchid\Filters\Filterable;

class Post extends Model
{
    use Filterable;

    /**
     * 可以在 URL 中使用排序的属性。
     *
     * @var array
     */
    protected $allowedSorts = [
        'id',
        'user_id',
        'rating',
        'publish_at',
        'created_at',
        'deleted_at',
    ];
}

一旦定义了允许排序的列,您可以通过调用 filters() 方法轻松启用排序。此方法将根据提供的 HTTP 参数处理排序。例如:

Post::filters()->paginate();

现在,当您发出 HTTP 请求时,排序行为如下:

http://example.com/demo?sort=created_at
$model->orderBy('created_at', 'asc');

http://example.com/demo?sort=-created_at
$model->orderBy('created_at', 'desc');

默认排序

如果您希望为数据指定默认排序顺序,可以使用 defaultSort 方法。此方法允许您在没有通过 HTTP 请求提供特定排序参数时设置默认排序列。例如:

Post::filters()->defaultSort('id')->paginate();

您还可以指定排序方向作为第二个参数。例如:

Post::filters()->defaultSort('id', 'desc')->paginate();

HTTP 请求的自动排序和过滤不适用于通过关系获取的模型字段。如果需要基于此类字段进行排序或过滤,可以使用第三方包,例如 Eloquent Power Joins。此包可以帮助您解决此问题:

User::orderByPowerJoins('profile.city');
User::orderByPowerJoins('profile.city', 'desc');

然而,您需要手动处理 HTTP 参数 sortfilter,因为包不会自动识别字段名前的 - 符号表示降序排序,以及如何应用过滤器。您可以使用 “过滤器” 来完成此操作。此外,您应该仅使用包方法来基于通过关系访问的字段进行排序或过滤。

我们的朋友