Skip to content
全部文档

查询构建器

简介

查询构建器允许你定义一组复杂条件来筛选表格中的数据。它能处理无限嵌套的条件,你可以用「and」和「or」操作将它们组合在一起。

查询构建器筛选器查询构建器筛选器

要使用它,你需要定义一组用于筛选数据的「约束」。Filament 包含一些遵循常见数据类型的内置约束,你也可以定义自己的自定义约束。

你可以使用 QueryBuilder 筛选器将查询构建器添加到任意表格:

php
use Filament\Tables\Filters\QueryBuilder;
use Filament\QueryBuilder\Constraints\BooleanConstraint;
use Filament\QueryBuilder\Constraints\DateConstraint;
use Filament\QueryBuilder\Constraints\NumberConstraint;
use Filament\QueryBuilder\Constraints\RelationshipConstraint;
use Filament\QueryBuilder\Constraints\RelationshipConstraint\Operators\IsRelatedToOperator;
use Filament\QueryBuilder\Constraints\SelectConstraint;
use Filament\QueryBuilder\Constraints\TextConstraint;

QueryBuilder::make()
    ->constraints([
        TextConstraint::make('name'),
        BooleanConstraint::make('is_visible'),
        NumberConstraint::make('stock'),
        SelectConstraint::make('status')
            ->options([
                'draft' => 'Draft',
                'reviewing' => 'Reviewing',
                'published' => 'Published',
            ])
            ->multiple(),
        DateConstraint::make('created_at'),
        RelationshipConstraint::make('categories')
            ->multiple()
            ->selectable(
                IsRelatedToOperator::make()
                    ->titleAttribute('name')
                    ->searchable()
                    ->multiple(),
            ),
        NumberConstraint::make('reviews.rating')
            ->integer(),
    ])

当查询构建器嵌套较深时,你可能需要增加筛选器可占用的空间。一种做法是将筛选器放在表格内容上方

php
use Filament\Tables\Enums\FiltersLayout;
use Filament\Tables\Filters\QueryBuilder;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->filters([
            QueryBuilder::make()
                ->constraints([
                    // ...
                ]),
        ], layout: FiltersLayout::AboveContent);
}

可用约束

Filament 附带多种可开箱即用的约束。你也可以创建自己的自定义约束

  • [文本约束](#text-constraints)
  • [布尔约束](#boolean-constraints)
  • [数字约束](#number-constraints)
  • [日期约束](#date-constraints)
  • [选择约束](#select-constraints)
  • [关联约束](#relationship-constraints)

文本约束

文本约束允许你筛选文本字段。它们可用于筛选任意文本字段,包括通过关联。

php
use Filament\QueryBuilder\Constraints\TextConstraint;

TextConstraint::make('name') // Filter the `name` column

TextConstraint::make('creator.name') // Filter the `name` column on the `creator` relationship using dot syntax

默认提供以下运算符:

  • 包含 - 筛选列使其包含搜索词
  • 不包含 - 筛选列使其不包含搜索词
  • 以…开头 - 筛选列使其以搜索词开头
  • 不以…开头 - 筛选列使其不以搜索词开头
  • 以…结尾 - 筛选列使其以搜索词结尾
  • 不以…结尾 - 筛选列使其不以搜索词结尾
  • 等于 - 筛选列使其等于搜索词
  • 不等于 - 筛选列使其不等于搜索词
  • 已填写 - 筛选列使其非空
  • 为空 - 筛选列使其为空

布尔约束

布尔约束允许你筛选布尔字段。它们可用于筛选任意布尔字段,包括通过关联。

php
use Filament\QueryBuilder\Constraints\BooleanConstraint;

BooleanConstraint::make('is_visible') // Filter the `is_visible` column

BooleanConstraint::make('creator.is_admin') // Filter the `is_admin` column on the `creator` relationship using dot syntax

默认提供以下运算符:

  • 为真 - 筛选列使其为 `true`
  • 为假 - 筛选列使其为 `false`

数字约束

数字约束允许你筛选数值字段。它们可用于筛选任意数值字段,包括通过关联。

php
use Filament\QueryBuilder\Constraints\NumberConstraint;

NumberConstraint::make('stock') // Filter the `stock` column

NumberConstraint::make('orders.item_count') // Filter the `item_count` column on the `orders` relationship using dot syntax

默认提供以下运算符:

  • 最小值 - 筛选列使其大于或等于搜索数字
  • 小于 - 筛选列使其小于搜索数字
  • 最大值 - 筛选列使其小于或等于搜索数字
  • 大于 - 筛选列使其大于搜索数字
  • 等于 - 筛选列使其等于搜索数字
  • 不等于 - 筛选列使其不等于搜索数字
  • 已填写 - 筛选列使其非空
  • 为空 - 筛选列使其为空

当对关联列使用数字约束时,用户还可以「聚合」关联记录。这意味着他们可以一次性按所有关联记录的总和、平均值、最小值或最大值来筛选该列。

整数约束

默认情况下,数字约束允许小数值。若只允许整数值,可以使用 integer() 方法:

php
use Filament\QueryBuilder\Constraints\NumberConstraint;

NumberConstraint::make('stock')
    ->integer()

日期约束

日期约束允许你筛选日期字段。它们可用于筛选任意日期字段,包括通过关联。

php
use Filament\QueryBuilder\Constraints\DateConstraint;

DateConstraint::make('created_at') // Filter the `created_at` column

DateConstraint::make('creator.created_at') // Filter the `created_at` column on the `creator` relationship using dot syntax

默认提供以下运算符:

  • 晚于 - 筛选列使其晚于搜索日期
  • 不晚于 - 筛选列使其不晚于搜索日期,或为同一日期
  • 早于 - 筛选列使其早于搜索日期
  • 不早于 - 筛选列使其不早于搜索日期,或为同一日期
  • 为某日期 - 筛选列使其与搜索日期相同
  • 不为某日期 - 筛选列使其与搜索日期不同
  • 为某月 - 筛选列使其与所选月份相同
  • 不为某月 - 筛选列使其与所选月份不同
  • 为某年 - 筛选列使其与搜索年份相同
  • 不为某年 - 筛选列使其与搜索年份不同

日期时间约束

默认情况下,日期约束只按日期筛选。若你有日期时间列并希望启用基于时间的筛选,可以使用 time() 方法:

php
use Filament\QueryBuilder\Constraints\DateConstraint;

DateConstraint::make('published_at')
    ->time()

选择约束

选择约束允许你使用选择字段来筛选字段。它们可用于筛选任意字段,包括通过关联。

php
use Filament\QueryBuilder\Constraints\SelectConstraint;

SelectConstraint::make('status') // Filter the `status` column
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])

SelectConstraint::make('creator.department') // Filter the `department` column on the `creator` relationship using dot syntax
    ->options([
        'sales' => 'Sales',
        'marketing' => 'Marketing',
        'engineering' => 'Engineering',
        'purchasing' => 'Purchasing',
    ])

DANGER

options() 只是 UI 呈现手段,不是授权边界。选项列表限制下拉框显示的内容,但约束运行前不会对照该列表校验提交值。查询构建器规则存储在 Livewire 状态中,可能被篡改——精心构造的请求可以向约束的 settings 提交任意值,而 IsOperator::apply() 会将其直接传入查询的 whereIn/where

若你用 options() 向某类用户隐藏某些值(例如对非管理员隐藏 archived),应改为限定底层查询本身——例如在约束上使用 modifyRelationshipQueryUsing()、在资源上使用 modifyQueryUsing(),或在模型上使用全局作用域——这样无论约束提交什么,受限制的行都不可达。

可搜索的选择约束

默认情况下,选择约束不允许用户搜索选项。若希望允许用户搜索选项,可以使用 searchable() 方法:

php
use Filament\QueryBuilder\Constraints\SelectConstraint;

SelectConstraint::make('status')
    ->searchable()
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])

多选约束

默认情况下,选择约束只允许用户选择单个选项。若希望允许用户选择多个选项,可以使用 multiple() 方法:

php
use Filament\QueryBuilder\Constraints\SelectConstraint;

SelectConstraint::make('status')
    ->multiple()
    ->options([
        'draft' => 'Draft',
        'reviewing' => 'Reviewing',
        'published' => 'Published',
    ])

当用户选择多个选项时,表格会筛选出匹配任一所选选项的记录。

关联约束

关联约束允许你使用关联相关数据来筛选字段:

php
use Filament\QueryBuilder\Constraints\RelationshipConstraint;
use Filament\QueryBuilder\Constraints\RelationshipConstraint\Operators\IsRelatedToOperator;

RelationshipConstraint::make('creator') // Filter the `creator` relationship
    ->selectable(
        IsRelatedToOperator::make()
            ->titleAttribute('name')
            ->searchable()
            ->multiple(),
    )

IsRelatedToOperator 用于配置「Is / Contains」和「Is not / Does not contain」运算符。它提供一个选择字段,让用户按附加到该关联的记录来筛选。titleAttribute() 方法用于指定在列表中标识每条关联记录的属性。searchable() 方法使列表可搜索。multiple() 方法允许用户选择多条关联记录;若选择了多条,表格会筛选出匹配任一所选关联记录的记录。

多重关联

默认情况下,关联约束只包含适合筛选单一关联(如 BelongsTo)的运算符。若你有 HasManyBelongsToMany 等关联,可能希望将约束标记为 multiple()

php
use Filament\QueryBuilder\Constraints\RelationshipConstraint;

RelationshipConstraint::make('categories')
    ->multiple()

这会向约束添加以下运算符:

  • 至少有 - 筛选列使其至少有指定数量的关联记录
  • 少于 - 筛选列使其关联记录数少于指定数量
  • 至多有 - 筛选列使其至多有指定数量的关联记录
  • 多于 - 筛选列使其关联记录数多于指定数量
  • 有 - 筛选列使其有指定数量的关联记录
  • 没有 - 筛选列使其没有指定数量的关联记录

空关联约束

RelationshipConstraint 不像其他约束那样支持 nullable()

若关联为 multiple(),约束会显示一个筛除「空」关联的选项,即该关联没有任何关联记录。若关联是单一的,可以使用 emptyable() 方法显示筛除「空」关联的选项:

php
use Filament\QueryBuilder\Constraints\RelationshipConstraint;

RelationshipConstraint::make('creator')
    ->emptyable()

若你有必须始终至少有 1 条关联记录的 multiple() 关联,可以使用 emptyable(false) 方法隐藏筛除「空」关联的选项:

php
use Filament\QueryBuilder\Constraints\RelationshipConstraint;

RelationshipConstraint::make('categories')
    ->emptyable(false)

可空约束

默认情况下,约束不会显示筛选 null 值的选项。若希望显示筛选 null 值的选项,可以使用 nullable() 方法:

php
use Filament\QueryBuilder\Constraints\TextConstraint;

TextConstraint::make('name')
    ->nullable()

现在还会提供以下运算符:

  • 已填写 - 筛选列使其非空
  • 为空 - 筛选列使其为空

限制规则树的大小

用户构建的规则集存储在 Livewire 组件的状态中,并随每次请求提交。为防止包含过大或嵌套过深的规则集的请求消耗过多服务器内存或 CPU,你可以限制规则数量及其嵌套深度。

默认不应用任何限制。你可以使用 maxRules()maxNestingDepth() 方法设置它们:

php
use Filament\Tables\Filters\QueryBuilder;

QueryBuilder::make()
    ->maxRules(100)
    ->maxNestingDepth(10)
    ->constraints([
        // ...
    ])

这两个方法也接受函数以动态计算该值:

php
use Filament\Tables\Filters\QueryBuilder;

QueryBuilder::make()
    ->maxRules(fn (): int => auth()->user()->isAdmin() ? 200 : 50)
    ->constraints([
        // ...
    ])

maxRules() 限制只统计单独的条件。「OR」组是结构容器,本身不计入该限制——嵌套深度由 maxNestingDepth() 单独限制。

设置限制后,UI 会强制执行:一旦达到最大规则数,「add rule」和克隆按钮会禁用,并显示说明原因的工具提示;一旦达到最大嵌套深度,「OR」分组选项会被隐藏。作为对绕过 UI 的篡改请求的防护,若提交的规则树仍超出任一限制,会被安全忽略,不应用任何约束,而不是筛选表格。

TIP

由于这些限制对每个查询构建器都相同,通常希望全局应用它们,而不是在每个筛选器上重复。你可以在服务提供者的 boot() 方法中使用 configureUsing() 来做到这一点,从而约束应用中的每个查询构建器:

php
use Filament\Tables\Filters\QueryBuilder;

QueryBuilder::configureUsing(function (QueryBuilder $queryBuilder): void {
    $queryBuilder
        ->maxRules(100)
        ->maxNestingDepth(10);
});

限定关联范围

使用关联约束时,你可以使用 modifyRelationshipQueryUsing() 方法限定关联范围以筛选关联记录:

php
use Filament\QueryBuilder\Constraints\TextConstraint;
use Illuminate\Database\Eloquent\Builder;

TextConstraint::make('creator.name')
    ->label('Admin creator name')
    ->modifyRelationshipQueryUsing(fn (Builder $query) => $query->where('is_admin', true))

自定义约束图标

每种约束类型都有默认图标,显示在选择器中标签旁边。你可以通过向 icon() 方法传入图标名称来自定义约束的图标:

php
use Filament\QueryBuilder\Constraints\TextConstraint;

TextConstraint::make('author.name')
    ->icon('heroicon-m-user')

覆盖默认运算符

每种约束类型都有一组默认运算符,你可以使用 operators() 方法自定义:

php
use Filament\QueryBuilder\Constraints\Operators\IsFilledOperator;
use Filament\QueryBuilder\Constraints\TextConstraint;

TextConstraint::make('author.name')
    ->operators([
        IsFilledOperator::make(),
    ])

这会移除所有运算符,并注册 EqualsOperator

若希望在列表末尾添加运算符,改用 pushOperators()

php
use Filament\QueryBuilder\Constraints\Operators\IsFilledOperator;
use Filament\QueryBuilder\Constraints\TextConstraint;

TextConstraint::make('author.name')
    ->pushOperators([
        IsFilledOperator::class,
    ])

若希望在列表开头添加运算符,改用 unshiftOperators()

php
use Filament\QueryBuilder\Constraints\Operators\IsFilledOperator;
use Filament\QueryBuilder\Constraints\TextConstraint;

TextConstraint::make('author.name')
    ->unshiftOperators([
        IsFilledOperator::class,
    ])

创建自定义约束

自定义约束可以与其他约束「内联」创建,使用 Constraint::make() 方法。你还应向 icon() 方法传入一个图标

php
use Filament\QueryBuilder\Constraints\Constraint;

Constraint::make('subscribed')
    ->icon('heroicon-m-bell')
    ->operators([
        // ...
    ]),

若要自定义约束的标签,可以使用 label() 方法:

php
use Filament\QueryBuilder\Constraints\Constraint;

Constraint::make('subscribed')
    ->label('Subscribed to updates')
    ->icon('heroicon-m-bell')
    ->operators([
        // ...
    ]),

现在,你必须为约束定义运算符。这些是可用于筛选该列的选项。若该列可空,你也可以为自定义约束注册该内置运算符:

php
use Filament\QueryBuilder\Constraints\Constraint;
use Filament\QueryBuilder\Constraints\Operators\IsFilledOperator;

Constraint::make('subscribed')
    ->label('Subscribed to updates')
    ->icon('heroicon-m-bell')
    ->operators([
        // ...
        IsFilledOperator::class,
    ]),

创建自定义运算符

自定义运算符可以使用 Operator::make() 方法创建:

php
use Filament\QueryBuilder\Constraints\Operators\Operator;

Operator::make('subscribed')
    ->label(fn (bool $isInverse): string => $isInverse ? 'Not subscribed' : 'Subscribed')
    ->summary(fn (bool $isInverse): string => $isInverse ? 'You are not subscribed' : 'You are subscribed')
    ->baseQuery(fn (Builder $query, bool $isInverse) => $query->{$isInverse ? 'whereDoesntHave' : 'whereHas'}(
        'subscriptions.user',
        fn (Builder $query) => $query->whereKey(auth()->user()),
    )),

在此示例中,该运算符可根据已认证用户是否订阅该记录来筛选记录。订阅记录在表格的 subscriptions 关联中。

baseQuery() 方法用于定义筛选记录的查询。选择「Subscribed」选项时 $isInversefalse,选择「Not subscribed」选项时为 true。该函数应用于表格的基础查询,可在其中使用 whereHas()。若你的函数无需应用于表格的基础查询(例如只使用简单的 where()whereIn()),可以改用 query() 方法,其额外好处是能在嵌套的「OR」组中使用。

label() 方法用于在运算符选择器中渲染选项。每个运算符会注册两个选项:一个用于未反转时,一个用于已反转时。

summary() 方法在约束应用于查询时用于约束的标题,以提供当前活动约束的概览。

自定义约束选择器

更改约束选择器中的列数

约束选择器默认只有 1 列。你可以通过向 constraintPickerColumns() 传入列数来自定义:

php
use Filament\Tables\Filters\QueryBuilder;

QueryBuilder::make()
    ->constraintPickerColumns(2)
    ->constraints([
        // ...
    ])

该方法可以有几种不同用法:

  • 你可以传入整数,例如 `constraintPickerColumns(2)`。该整数是 `lg` 断点及以上使用的列数。所有更小的设备将只有 1 列。
  • 你可以传入数组,其中键为断点、值为列数。例如,`constraintPickerColumns(['md' => 2, 'xl' => 4])` 会在中等设备上创建 2 列布局,在超大设备上创建 4 列布局。更小设备的默认断点使用 1 列,除非你使用 `default` 数组键。

断点(smmdlgxl2xl)由 Tailwind 定义,可在 Tailwind 文档 中找到。

增大约束选择器的宽度

当你增加列数时,下拉菜单的宽度应逐步增大以容纳额外列。若需要更多控制,可以使用 constraintPickerWidth() 方法手动为下拉菜单设置最大宽度。选项对应 Tailwind 的 max-width 比例。可选值为 xssmmdlgxl2xl3xl4xl5xl6xl7xl

php
use Filament\Tables\Filters\QueryBuilder;

QueryBuilder::make()
    ->constraintPickerColumns(3)
    ->constraintPickerWidth('2xl')
    ->constraints([
        // ...
    ])