Skip to content
全部文档

构建器

简介

重复器 类似,构建器组件允许你输出由重复表单组件组成的 JSON 数组。与只定义一套可重复表单 schema 的重复器不同,构建器允许你定义不同的 schema「块(blocks)」,并可以任意顺序重复。这使其适用于构建更高级的数组结构。

构建器组件的主要用途是使用预定义块构建网页内容。这可以是营销网站的内容,甚至可以是在线表单中的字段。下面的示例为页面内容中的不同元素定义了多个块。在网站前端,你可以遍历 JSON 中的每个块并按需格式化。

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;

Builder::make('content')
    ->blocks([
        Block::make('heading')
            ->schema([
                TextInput::make('content')
                    ->label('Heading')
                    ->required(),
                Select::make('level')
                    ->options([
                        'h1' => 'Heading 1',
                        'h2' => 'Heading 2',
                        'h3' => 'Heading 3',
                        'h4' => 'Heading 4',
                        'h5' => 'Heading 5',
                        'h6' => 'Heading 6',
                    ])
                    ->required(),
            ])
            ->columns(2),
        Block::make('paragraph')
            ->schema([
                Textarea::make('content')
                    ->label('Paragraph')
                    ->required(),
            ]),
        Block::make('image')
            ->schema([
                FileUpload::make('url')
                    ->label('Image')
                    ->image()
                    ->required(),
                TextInput::make('alt')
                    ->label('Alt text')
                    ->required(),
            ]),
    ])
构建器构建器

我们建议用数据库中的 JSON 列存储构建器数据。此外,若使用 Eloquent,请确保该列有 array 类型转换(cast)。

从上例可见,块可在组件的 blocks() 方法中定义。块是 Builder\Block 对象,需要唯一名称以及组件 schema:

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\TextInput;

Builder::make('content')
    ->blocks([
        Block::make('heading')
            ->schema([
                TextInput::make('content')->required(),
                // ...
            ]),
        // ...
    ])

设置块的标签

默认情况下,块的标签会根据其名称自动确定。要覆盖块的标签,可以使用 label() 方法。以这种方式自定义标签在希望使用 本地化翻译字符串 时很有用:

php
use Filament\Forms\Components\Builder\Block;

Block::make('heading')
    ->label(__('blocks.heading'))

根据内容为构建器项设置标签

你可以使用同一个 label() 方法为构建器项添加标签。该方法接受一个闭包,在 $state 变量中接收该项的数据。若 $state 为 null,应返回在块选择器中显示的块标签。否则,应返回用作该项标签的字符串:

php
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\TextInput;

Block::make('heading')
    ->schema([
        TextInput::make('content')
            ->live(onBlur: true)
            ->required(),
        // ...
    ])
    ->label(function (?array $state): string {
        if ($state === null) {
            return 'Heading';
        }

        return $state['content'] ?? 'Untitled heading';
    })

若希望在使用表单时实时看到项标签更新,从 $state 使用的任何字段都应设为 live()

TIP

你可以将各种工具(utilities)作为参数注入到传给 label() 的函数中。

根据内容为块设置标签的构建器根据内容为块设置标签的构建器

为构建器项编号

默认情况下,构建器中的项在其标签旁会有编号。你可以使用 blockNumbers(false) 方法禁用它:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->blockNumbers(false)

TIP

除了接受静态值,blockNumbers() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

设置块的图标

块也可以有 图标,显示在标签旁。你可以通过向 icon() 方法传入图标名称来添加图标:

php
use Filament\Forms\Components\Builder\Block;
use Filament\Support\Icons\Heroicon;

Block::make('paragraph')
    ->icon(Heroicon::Bars3BottomLeft)

TIP

除了接受静态值,icon() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

下拉菜单中带块图标的构建器下拉菜单中带块图标的构建器

在块的页眉中添加图标

默认情况下,构建器中的块在页眉标签旁没有图标,图标仅出现在添加新块的下拉菜单中。你可以使用 blockIcons() 方法启用它:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->blockIcons()

可选地,你可以向 blockIcons() 方法传入布尔值,以控制是否在块页眉中显示图标:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->blockIcons(FeatureFlag::active())

TIP

除了接受静态值,blockIcons() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

块页眉中带图标的构建器块页眉中带图标的构建器

预览块

若你更希望在构建器中渲染只读预览而非块的表单,可以使用 blockPreviews() 方法。这将渲染每个块的 preview() 而非表单。块数据会以同名变量传给预览 Blade 视图:

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\TextInput;

Builder::make('content')
    ->blockPreviews()
    ->blocks([
        Block::make('heading')
            ->schema([
                TextInput::make('text')
                    ->placeholder('Default heading'),
            ])
            ->preview('filament.content.block-previews.heading'),
    ])

/resources/views/filament/content/block-previews/heading.blade.php 中,你可以像这样访问块数据:

blade
<h1>
    {{ $text ?? 'Default heading' }}
</h1>

可选地,blockPreviews() 方法接受布尔值,以控制构建器是否应渲染块预览:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->blockPreviews(FeatureFlag::active())

TIP

除了接受静态值,blockPreviews()preview() 方法也接受函数以动态计算这些值。你可以将各种工具(utilities)作为参数注入到函数中。

带块预览的构建器带块预览的构建器

可交互的块预览

默认情况下,预览内容不可交互,点击会打开该块的编辑(Edit)模态框以管理其设置。若希望块预览中的链接与按钮保持可交互,可以使用 blockPreviews() 方法的 areInteractive: true 参数:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blockPreviews(areInteractive: true)
    ->blocks([
        //
    ])

TIP

除了接受静态值,areInteractive 参数也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

添加项

构建器下方会显示一个操作按钮,允许用户添加新项。

设置添加操作按钮的标签

你可以使用 addActionLabel() 方法设置标签,以自定义添加构建器项的按钮上应显示的文本:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->addActionLabel('Add a new block')

TIP

除了接受静态值,addActionLabel() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

对齐添加操作按钮

默认情况下,添加操作居中对齐。你可以使用 addActionAlignment() 方法调整,传入 Alignment::StartAlignment::EndAlignment 选项:

php
use Filament\Forms\Components\Builder;
use Filament\Support\Enums\Alignment;

Builder::make('content')
    ->schema([
        // ...
    ])
    ->addActionAlignment(Alignment::Start)

TIP

除了接受静态值,addActionAlignment() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

添加操作对齐到起始端的构建器添加操作对齐到起始端的构建器

阻止用户添加项

你可以使用 addable(false) 方法阻止用户向构建器添加项:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->addable(false)

TIP

除了接受静态值,addable() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

删除项

每个项上会显示一个操作按钮,允许用户删除它。

阻止用户删除项

你可以使用 deletable(false) 方法阻止用户从构建器中删除项:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->deletable(false)

TIP

除了接受静态值,deletable() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

重新排序项

每个项上会显示一个按钮,允许用户通过拖放在列表中重新排序。

阻止用户重新排序项

你可以使用 reorderable(false) 方法阻止用户对构建器中的项重新排序:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->reorderable(false)

TIP

除了接受静态值,reorderable() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

使用按钮重新排序项

你可以使用 reorderableWithButtons() 方法,启用通过上下移动按钮重新排序项:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->reorderableWithButtons()
可通过按钮重新排序的构建器可通过按钮重新排序的构建器

可选地,你可以传入布尔值,以控制构建器是否应通过按钮排序:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->reorderableWithButtons(FeatureFlag::active())

TIP

除了接受静态值,reorderableWithButtons() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

阻止通过拖放重新排序

你可以使用 reorderableWithDragAndDrop(false) 方法阻止通过拖放对项排序:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->reorderableWithDragAndDrop(false)

TIP

除了接受静态值,reorderableWithDragAndDrop() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

折叠项

构建器可以使用 collapsible(),以便在长表单中可选地隐藏内容:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->collapsible()
可折叠的构建器可折叠的构建器

你也可以默认折叠所有项:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->collapsed()
默认折叠的构建器默认折叠的构建器

可选地,collapsible()collapsed() 方法接受布尔值,以控制构建器是否可折叠以及是否默认折叠:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->collapsible(FeatureFlag::active())
    ->collapsed(FeatureFlag::active())

TIP

除了接受静态值,collapsible()collapsed() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

若折叠项包含渲染成本较高的组件,你可以 延迟加载其块 schema,直到它们被展开。

延迟加载块 schema

若块的 schema 渲染成本较高,可以向 schema() 传入 Schema 对象并使用 deferLoading()。当项默认 折叠 时尤其有用。每个块项的 schema 会在该项展开并进入视口时独立加载:

php
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;

Builder::make('content')
    ->blocks([
        Block::make('heading')
            ->schema(
                Schema::make()
                    ->components([
                        TextInput::make('content')
                            ->label('Heading')
                            ->required(),
                    ])
                    ->deferLoading(),
            ),
        // ...
    ])
    ->collapsed()

构建器块项 schema 会从其项状态路径自动获得唯一键。你可以在 schema 概览 中了解有关延迟 schema 的更多信息。

克隆项

你可以使用 cloneable() 方法允许构建器项被复制:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->cloneable()
可克隆的构建器可克隆的构建器

自定义块选择器

更改块选择器中的列数

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

php
use Filament\Forms\Components\Builder;

Builder::make()
    ->blockPickerColumns(2)
    ->blocks([
        // ...
    ])

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

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

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

TIP

除了接受静态值,blockPickerColumns() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

块选择器为 2 列的构建器块选择器为 2 列的构建器

增大块选择器的宽度

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

php
use Filament\Forms\Components\Builder;

Builder::make()
    ->blockPickerColumns(3)
    ->blockPickerWidth('2xl')
    ->blocks([
        // ...
    ])

TIP

除了接受静态值,blockPickerWidth() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

限制块可使用的次数

默认情况下,每个块可在构建器中无限次使用。你可以使用块上的 maxItems() 方法限制次数:

php
use Filament\Forms\Components\Builder\Block;

Block::make('heading')
    ->schema([
        // ...
    ])
    ->maxItems(1)

TIP

除了接受静态值,maxItems() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。

使用 $get() 访问父字段值

所有表单组件都能 使用 $get()$set() 访问另一个字段的值。但在构建器的 schema 内使用时,你可能会遇到意外行为。

这是因为 $get()$set() 默认限定在当前构建器项的作用域内。这意味着你可以轻松与该构建器项内的另一个字段交互,而无需知道当前表单组件属于哪个构建器项。

其后果是,当你无法与构建器外的字段交互时可能会感到困惑。我们使用 ../ 语法解决此问题——$get('../parent_field_name')

假设你的表单具有以下数据结构:

php
[
    'client_id' => 1,

    'builder' => [
        'item1' => [
            'service_id' => 2,
        ],
    ],
]

你正尝试从构建器项内部检索 client_id 的值。

$get() 相对于当前构建器项,因此 $get('client_id') 实际在查找 $get('builder.item1.client_id')

你可以使用 ../ 在数据结构中向上一级,因此 $get('../client_id')$get('builder.client_id'),而 $get('../../client_id')$get('client_id')

无参数的 $get(),或 $get('')$get('./') 的特殊情况,将始终返回当前构建器项的完整数据数组。

构建器验证

除了 验证 页面列出的所有规则外,还有专门针对构建器的附加规则。

项数量验证

你可以通过设置 minItems()maxItems() 方法,验证构建器中可拥有的最小与最大项数:

php
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->minItems(1)
    ->maxItems(5)

TIP

除了接受静态值,minItems()maxItems() 方法也接受函数以动态计算这些值。你可以将各种工具(utilities)作为参数注入到函数中。

自定义构建器项操作

该字段使用操作对象,以便轻松自定义其中的按钮。你可以通过向操作注册方法传入函数来自定义这些按钮。该函数可访问 $action 对象,你可以用它来 自定义操作。以下方法可用于自定义操作:

  • addAction()
  • addBetweenAction()
  • cloneAction()
  • collapseAction()
  • collapseAllAction()
  • deleteAction()
  • expandAction()
  • expandAllAction()
  • moveDownAction()
  • moveUpAction()
  • reorderAction()

以下是自定义操作的示例:

php
use Filament\Actions\Action;
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->collapseAllAction(
        fn (Action $action) => $action->label('Collapse all content'),
    )

TIP

操作注册方法可以将各种工具(utilities)作为参数注入到函数中。

用模态框确认构建器操作

你可以通过在操作对象上使用 requiresConfirmation() 方法,用模态框确认操作。你可以使用任意 模态框自定义方法 更改其内容与行为:

php
use Filament\Actions\Action;
use Filament\Forms\Components\Builder;

Builder::make('content')
    ->blocks([
        // ...
    ])
    ->deleteAction(
        fn (Action $action) => $action->requiresConfirmation(),
    )

INFO

addAction()addBetweenAction()collapseAction()collapseAllAction()expandAction()expandAllAction()reorderAction() 方法不支持确认模态框,因为点击其按钮不会发起显示模态框所需的网络请求。

向构建器添加额外项操作

你可以通过向 extraItemActions() 传入 Action 对象,为每个构建器项的页眉添加新的 操作按钮

php
use Filament\Actions\Action;
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\TextInput;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Facades\Mail;

Builder::make('content')
    ->blocks([
        Block::make('contactDetails')
            ->schema([
                TextInput::make('email')
                    ->label('Email address')
                    ->email()
                    ->required(),
                // ...
            ]),
        // ...
    ])
    ->extraItemActions([
        Action::make('sendEmail')
            ->icon(Heroicon::Square2Stack)
            ->action(function (array $arguments, Builder $component): void {
                $itemData = $component->getItemState($arguments['item']);
                
                Mail::to($itemData['email'])
                    ->send(
                        // ...
                    );
            }),
    ])

在此示例中,$arguments['item'] 给出当前构建器项的 ID。你可以使用构建器组件上的 getItemState() 方法验证该构建器项中的数据。该方法返回该项的已验证数据。若该项无效,将取消操作并在表单中为该项显示错误消息。

若希望获取当前项的原始数据而不进行验证,可以改用 $component->getRawItemState($arguments['item'])

若希望操作整个构建器的原始数据(例如添加、移除或修改项),可以使用 $component->getState() 获取数据,并用 $component->state($state) 重新设置:

php
use Illuminate\Support\Str;

// Get the raw data for the entire builder
$state = $component->getState();

// Add an item, with a random UUID as the key
$state[Str::uuid()] = [
    'type' => 'contactDetails',
    'data' => [
        'email' => auth()->user()->email,
    ],
];

// Set the new data for the builder
$component->state($state);