构建器
简介
与 重复器 类似,构建器组件允许你输出由重复表单组件组成的 JSON 数组。与只定义一套可重复表单 schema 的重复器不同,构建器允许你定义不同的 schema「块(blocks)」,并可以任意顺序重复。这使其适用于构建更高级的数组结构。
构建器组件的主要用途是使用预定义块构建网页内容。这可以是营销网站的内容,甚至可以是在线表单中的字段。下面的示例为页面内容中的不同元素定义了多个块。在网站前端,你可以遍历 JSON 中的每个块并按需格式化。
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:
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() 方法。以这种方式自定义标签在希望使用 本地化翻译字符串 时很有用:
use Filament\Forms\Components\Builder\Block;
Block::make('heading')
->label(__('blocks.heading'))根据内容为构建器项设置标签
你可以使用同一个 label() 方法为构建器项添加标签。该方法接受一个闭包,在 $state 变量中接收该项的数据。若 $state 为 null,应返回在块选择器中显示的块标签。否则,应返回用作该项标签的字符串:
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) 方法禁用它:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->blockNumbers(false)TIP
除了接受静态值,blockNumbers() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
设置块的图标
块也可以有 图标,显示在标签旁。你可以通过向 icon() 方法传入图标名称来添加图标:
use Filament\Forms\Components\Builder\Block;
use Filament\Support\Icons\Heroicon;
Block::make('paragraph')
->icon(Heroicon::Bars3BottomLeft)TIP
除了接受静态值,icon() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。


在块的页眉中添加图标
默认情况下,构建器中的块在页眉标签旁没有图标,图标仅出现在添加新块的下拉菜单中。你可以使用 blockIcons() 方法启用它:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->blockIcons()可选地,你可以向 blockIcons() 方法传入布尔值,以控制是否在块页眉中显示图标:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->blockIcons(FeatureFlag::active())TIP
除了接受静态值,blockIcons() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。


预览块
若你更希望在构建器中渲染只读预览而非块的表单,可以使用 blockPreviews() 方法。这将渲染每个块的 preview() 而非表单。块数据会以同名变量传给预览 Blade 视图:
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 中,你可以像这样访问块数据:
<h1>
{{ $text ?? 'Default heading' }}
</h1>可选地,blockPreviews() 方法接受布尔值,以控制构建器是否应渲染块预览:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->blockPreviews(FeatureFlag::active())TIP
除了接受静态值,blockPreviews() 与 preview() 方法也接受函数以动态计算这些值。你可以将各种工具(utilities)作为参数注入到函数中。


可交互的块预览
默认情况下,预览内容不可交互,点击会打开该块的编辑(Edit)模态框以管理其设置。若希望块预览中的链接与按钮保持可交互,可以使用 blockPreviews() 方法的 areInteractive: true 参数:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blockPreviews(areInteractive: true)
->blocks([
//
])TIP
除了接受静态值,areInteractive 参数也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
添加项
构建器下方会显示一个操作按钮,允许用户添加新项。
设置添加操作按钮的标签
你可以使用 addActionLabel() 方法设置标签,以自定义添加构建器项的按钮上应显示的文本:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->addActionLabel('Add a new block')TIP
除了接受静态值,addActionLabel() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
对齐添加操作按钮
默认情况下,添加操作居中对齐。你可以使用 addActionAlignment() 方法调整,传入 Alignment::Start 或 Alignment::End 的 Alignment 选项:
use Filament\Forms\Components\Builder;
use Filament\Support\Enums\Alignment;
Builder::make('content')
->schema([
// ...
])
->addActionAlignment(Alignment::Start)TIP
除了接受静态值,addActionAlignment() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。


阻止用户添加项
你可以使用 addable(false) 方法阻止用户向构建器添加项:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->addable(false)TIP
除了接受静态值,addable() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
删除项
每个项上会显示一个操作按钮,允许用户删除它。
阻止用户删除项
你可以使用 deletable(false) 方法阻止用户从构建器中删除项:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->deletable(false)TIP
除了接受静态值,deletable() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
重新排序项
每个项上会显示一个按钮,允许用户通过拖放在列表中重新排序。
阻止用户重新排序项
你可以使用 reorderable(false) 方法阻止用户对构建器中的项重新排序:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->reorderable(false)TIP
除了接受静态值,reorderable() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
使用按钮重新排序项
你可以使用 reorderableWithButtons() 方法,启用通过上下移动按钮重新排序项:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->reorderableWithButtons()

可选地,你可以传入布尔值,以控制构建器是否应通过按钮排序:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->reorderableWithButtons(FeatureFlag::active())TIP
除了接受静态值,reorderableWithButtons() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
阻止通过拖放重新排序
你可以使用 reorderableWithDragAndDrop(false) 方法阻止通过拖放对项排序:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->reorderableWithDragAndDrop(false)TIP
除了接受静态值,reorderableWithDragAndDrop() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
折叠项
构建器可以使用 collapsible(),以便在长表单中可选地隐藏内容:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->collapsible()

你也可以默认折叠所有项:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->collapsed()

可选地,collapsible() 与 collapsed() 方法接受布尔值,以控制构建器是否可折叠以及是否默认折叠:
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 会在该项展开并进入视口时独立加载:
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() 方法允许构建器项被复制:
use Filament\Forms\Components\Builder;
Builder::make('content')
->blocks([
// ...
])
->cloneable()

自定义块选择器
更改块选择器中的列数
块选择器默认只有 1 列。你可以通过向 blockPickerColumns() 传入列数来自定义:
use Filament\Forms\Components\Builder;
Builder::make()
->blockPickerColumns(2)
->blocks([
// ...
])该方法可以有几种不同用法:
- 你可以传入整数,例如 `blockPickerColumns(2)`。该整数是 `lg` 断点及以上使用的列数。所有更小的设备将只有 1 列。
- 你可以传入数组,其中键为断点、值为列数。例如,`blockPickerColumns(['md' => 2, 'xl' => 4])` 会在中等设备上创建 2 列布局,在超大设备上创建 4 列布局。更小设备的默认断点使用 1 列,除非你使用 `default` 数组键。
断点(sm、md、lg、xl、2xl)由 Tailwind 定义,可在 Tailwind 文档 中找到。
TIP
除了接受静态值,blockPickerColumns() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。


增大块选择器的宽度
当你 增加列数 时,下拉菜单的宽度应逐步增大以容纳额外列。若需要更多控制,可以使用 blockPickerWidth() 方法手动为下拉菜单设置最大宽度。选项对应 Tailwind 的 max-width 比例。可选值为 xs、sm、md、lg、xl、2xl、3xl、4xl、5xl、6xl、7xl:
use Filament\Forms\Components\Builder;
Builder::make()
->blockPickerColumns(3)
->blockPickerWidth('2xl')
->blocks([
// ...
])TIP
除了接受静态值,blockPickerWidth() 方法也接受函数以动态计算该值。你可以将各种工具(utilities)作为参数注入到函数中。
限制块可使用的次数
默认情况下,每个块可在构建器中无限次使用。你可以使用块上的 maxItems() 方法限制次数:
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')。
假设你的表单具有以下数据结构:
[
'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() 方法,验证构建器中可拥有的最小与最大项数:
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()
以下是自定义操作的示例:
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() 方法,用模态框确认操作。你可以使用任意 模态框自定义方法 更改其内容与行为:
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 对象,为每个构建器项的页眉添加新的 操作按钮:
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) 重新设置:
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);