资讯动态

Filament Forms Builder 组件详解:用 Block 构建可拖拽的页面内容编辑器

发布时间:2026/9/10 11:06:25 来源:尧图企业网站定制
Filament Forms Builder 组件详解用 Block 构建可拖拽的页面内容编辑器【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament导读Filament 是一套基于 Laravel Livewire 的开源 UI 框架其forms包提供了丰富的表单组件。其中Builder组件与 Repeater 类似会输出一组可重复的表单组件 JSON 数组但它的核心区别在于Builder 允许你预先定义多种不同的 schema「块」Block让用户在任意顺序、任意次数下自由组合。这使得它成为构建营销网站内容、在线表单字段、富内容页面等「内容块驱动」编辑器的理想方案。读完本文你将掌握 Builder 的完整 APIBlock 定义、标签与图标、块预览、增删改排序、折叠、克隆、块选择器自定义、数量限制、跨字段取值以及内置校验规则并了解其底层源码实现与测试验证。一、Builder 与 Repeater 的本质区别在 Filament 的表单体系中Repeater 与 Builder 常常被放在一起讨论但二者有明确的职责划分Repeater只定义一套表单 schema在列表中重复渲染多次适用于结构完全一致的重复数据如订单明细行Builder定义多套schema 块Block每套块拥有独立的字段结构用户可以在任意顺序下混合组合适用于「同一字段内包含多种异构结构」的场景。从源码上看Builder继承自Filament\Forms\Components\Field见 Builder.php并将传入的blocks()直接委托给组件容器——blocks()方法内部实现就是$this-components($blocks)即每个 Block 本质上是一个子组件Builder.php#L163-L168。Block则继承自Filament\Schemas\Components\Component见 Block.php。Builder 的典型应用场景构建网页内容。例如为一个营销官网定义 heading标题、paragraph段落、image图片等块前端拿到 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(), ]), ])数据存储建议官方明确建议Builder 的数据应当存储在数据库的JSON类型列中如果使用 Eloquent请务必给该列加上arraycast否则从模型读取时得到的是字符串而不是数组。生成的数据结构大致如下[ [ type heading, data [ content 欢迎访问本站, level h1, ], ], [ type paragraph, data [ content 这是一段正文……, ], ], ]每个条目包含type块名与data该块的字段数据两个键。前端即可据此遍历渲染。Block 的定义与唯一性要求Block 通过Block::make(name)创建名称必须全局唯一并提供一个组件 schema。从 Block.php#L26-L45 可以看出make()会校验名称非空否则抛出InvalidArgumentExceptionuse 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(), // ... ]), // ... ])二、设置块的标签Label2.1 默认标签与label()覆盖默认情况下块标签会根据块名自动推导源码 Block.php#L90-L96块名经 kebab-case 转换、下划线替换为空格并首字母大写。若要覆盖默认标签使用label()方法——官方特别推荐结合 Laravel 的翻译字符串实现国际化use Filament\Forms\Components\Builder\Block; Block::make(heading) -label(__(blocks.heading))2.2 根据块内容动态生成条目标签同一个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()或至少live(onBlur: true)否则标签不会随输入实时更新。该闭包除了$state外还可以注入其他工具参数参数类型说明$keystring当前块条目的键UUID 或数字索引$indexint当前块条目的零基序号$statearraystring, mixed当前块条目的原始未校验数据对应地源码中Block::getLabel()的求值会同时传入index、key、state、uuid四个变量Block.php#L79-L101。2.3 关闭条目编号默认每个条目标签旁会显示一个序号1、2、3……可通过blockNumbers(false)关闭use Filament\Forms\Components\Builder; Builder::make(content) -blocks([ // ... ]) -blockNumbers(false)blockNumbers()同样支持传闭包动态计算。底层上序号在渲染时通过$itemIndex输出见 Builder.php#L1596-L1598而该开关对应属性$hasBlockNumbers默认值为trueBuilder.php#L61。三、设置块的图标Icon3.1 为块添加图标块可以设置图标展示在块选择器的下拉列表中、标签旁边use Filament\Forms\Components\Builder\Block; use Filament\Support\Icons\Heroicon; Block::make(paragraph) -icon(Heroicon::Bars3BottomLeft)这里使用的是 Filament 内置的Heroicon枚举也可以在 图标文档 查看其他引入图标的方式。icon()方法在源码中接受string | BackedEnum | Htmlable | Closure四种类型Block.php#L52-L62同样支持闭包动态计算。3.2 在块头部显示图标默认情况下图标只出现在「添加块」的下拉选择器中块头部并不显示图标。通过blockIcons()开启头部图标展示use Filament\Forms\Components\Builder; Builder::make(content) -blocks([ // ... ]) -blockIcons()也可以传入布尔值动态控制Builder::make(content) -blocks([ // ... ]) -blockIcons(FeatureFlag::active())对应源码属性$hasBlockIcons默认值为falseBuilder.php#L63当为true且块配置了图标时头部会渲染fi-fo-builder-item-header-iconBuilder.php#L1584-L1586。四、块预览Block Previews4.1 用只读预览替代表单当表单很长时你可能希望在 Builder 中直接展示块内容的只读预览而不是整块表单。使用blockPreviews()即可开启后每个块渲染的是preview()指定的 Blade 视图而不是它的表单。块的原始数据会以与字段同名的变量传入该 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), ])对应的 Blade 视图如resources/views/filament/content/block-previews/heading.blade.phph1 {{ $text ?? Default heading }} /h1blockPreviews()也可以传入布尔值动态控制显示与否Builder::make(content) -blocks([ // ... ]) -blockPreviews(FeatureFlag::active())底层实现中preview()存储在块的$preview属性renderPreview()通过view($previewView, $data)把块数据作为视图变量渲染见 HasPreview.php渲染逻辑位于 Builder.php#L1642-L1650——只要hasBlockPreviews()且块有预览就渲染预览区而非表单。4.2 交互式块预览默认情况下预览内容不可交互点击预览区域会打开该块的「编辑」模态框来管理设置。如果你希望预览中的链接、按钮保持可点击交互为blockPreviews()传入命名参数areInteractive: trueuse Filament\Forms\Components\Builder; Builder::make(content) -blockPreviews(areInteractive: true) -blocks([ // ])areInteractive参数同样接受闭包。从源码看交互式预览会为预览容器加上fi-interactive类并隐藏编辑遮罩层Builder.php#L1646-L1658。五、添加、删除、排序条目5.1 添加条目Builder 底部默认显示一个「添加」按钮点击后弹出块选择器选择块类型即可插入新条目。自定义添加按钮文案使用addActionLabel()。use Filament\Forms\Components\Builder; Builder::make(content) -blocks([ // ... ]) -addActionLabel(Add a new block)说明addActionLabel()也支持闭包动态计算。对应默认文案见源码getAddActionLabel()Builder.php#L988-L993默认会拼接字段标签如 Add content。调整添加按钮对齐方式默认居中可用addActionAlignment()配合Filament\Support\Enums\Alignment枚举改为左对齐Alignment::Start或右对齐Alignment::Enduse Filament\Forms\Components\Builder; use Filament\Support\Enums\Alignment; Builder::make(content) -schema([ // ... ]) -addActionAlignment(Alignment::Start)源码中getAddActionAlignment()会把字符串值转换为Alignment枚举Builder.php#L232-L241并对齐到下拉浮层的 placement。禁止添加addable(false)。Builder::make(content) -blocks([ // ... ]) -addable(false)5.2 删除条目每个条目头部默认显示删除按钮。禁止删除deletable(false)。Builder::make(content) -blocks([ // ... ]) -deletable(false)5.3 排序条目默认每个条目支持拖拽排序。禁止排序reorderable(false)。Builder::make(content) -blocks([ // ... ]) -reorderable(false)改用上下按钮排序reorderableWithButtons()也可传布尔值控制Builder::make(content) -blocks([ // ... ]) -reorderableWithButtons()Builder::make(content) -blocks([ // ... ]) -reorderableWithButtons(FeatureFlag::active())仅禁用拖拽保留按钮排序reorderableWithDragAndDrop(false)Builder::make(content) -blocks([ // ... ]) -reorderableWithDragAndDrop(false)源码佐证以上开关对应$isReorderable默认true、$isReorderableWithDragAndDrop默认true、$isReorderableWithButtons默认false三个属性Builder.php#L49-L53。拖拽手柄与上下移动按钮的可见性分别由isReorderableWithDragAndDrop()、isReorderableWithButtons()决定且都会与isReorderable()取与Builder.php#L1009-L1017。另外注意组件处于禁用disabled状态时添加、删除、排序功能会整体失效见isAddable()、isDeletable()、isReorderable()中isDisabled()的短路判断Builder.php#L1000-L1039。六、折叠与延迟加载6.1 折叠条目长表单中可以让 Builder 条目可折叠以隐藏冗长的字段Builder::make(content) -blocks([ // ... ]) -collapsible()还可以让所有条目默认折叠Builder::make(content) -blocks([ // ... ]) -collapsed()两者均可传入布尔值动态控制Builder::make(content) -blocks([ // ... ]) -collapsible(FeatureFlag::active()) -collapsed(FeatureFlag::active())注意从 Builder.php#L1487-L1505 可以看到当条目数 ≥ 2 且可折叠时Builder 顶部还会出现「全部折叠 / 全部展开」的快捷链接。6.2 延迟加载块 schemaDeferred Loading如果某些块的 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()Builder 的块条目 schema 会自动从其条目状态路径获得唯一 key。更多关于延迟 schema 的内容可参考 Schema 文档。七、克隆条目如果希望用户能快速复制已有条目包括其数据使用cloneable()Builder::make(content) -blocks([ // ... ]) -cloneable()克隆能力由CanBeClonedtrait 提供见 CanBeCloned.php默认$isCloneable false。克隆动作在源码中会为目标条目生成新的 UUID key 并复制其完整数据Builder.php#L338-L348当条目数已达maxItems上限或组件被禁用时克隆按钮不会渲染。八、自定义块选择器Block Picker添加条目时弹出的下拉即为「块选择器」默认只有 1 列可以通过以下两个方法自定义8.1 修改块选择器列数blockPickerColumns()Builder::make() -blockPickerColumns(2) -blocks([ // ... ])该方法有两种用法传整数如blockPickerColumns(2)该整数表示lg断点及以上使用的列数更小设备始终为 1 列传数组键为断点、值为列数。如blockPickerColumns([md 2, xl 4])表示中等屏幕 2 列、超大屏幕 4 列更小设备默认 1 列除非使用default键单独指定。断点sm、md、lg、xl、2xl由 Tailwind 定义。源码中getBlockPickerColumns()的默认结构即为[default 1, sm null, md null, lg null, xl null, 2xl null]Builder.php#L1132-L1139整数参数会自动归一化为[lg 整数]Builder.php#L1113-L1117。8.2 调整块选择器宽度blockPickerWidth()增加列数后下拉宽度会随列数按档位递增如需手动精确控制最大宽度使用blockPickerWidth()。可选值与 Tailwind 的 max-width 刻度对应xs、sm、md、lg、xl、2xl、3xl、4xl、5xl、6xl、7xlBuilder::make() -blockPickerColumns(3) -blockPickerWidth(2xl) -blocks([ // ... ])有趣的是源码会自动计算默认宽度2 列 →md、3 列 →2xl、4 列 →4xl、5 列 →6xl、6 列 →7xlBuilder.php#L1200-L1207手动设置即覆盖此默认。九、限制块的重复使用次数默认每个块可以在 Builder 中无限次使用。若要限制可在Block 上调用maxItems()use Filament\Forms\Components\Builder\Block; Block::make(heading) -schema([ // ... ]) -maxItems(1)注意这里的maxItems()是 Block 的方法Block.php#L64-L74用于限制同一块类型在 Builder 中出现的最大次数。当某块达到上限后它会从块选择器中消失getBlockPickerBlocks()会过滤掉已达上限的块见 Builder.php#L1079-L1100。该值也支持闭包动态计算。十、跨字段取值$get()/$set()的路径语义所有表单组件都可以用$get()/$set()读取/写入其他字段的值参见 Forms 概览但在 Builder 的 schema 内部使用时需要注意作用域问题。关键规则$get()/$set()默认以当前 Builder 条目为作用域。也就是说在某个块条目内部调用$get(foo)实际查找的是「当前条目下的foo」而不是 Builder 外部的foo。这一设计让你无需知道当前组件属于哪个条目就能轻松读取同一条目内的其他字段。其副作用是你可能无法直接访问 Builder 外部的字段。解决办法是使用../语法上跳一级$get(../parent_field_name)。考虑如下数据结构[ client_id 1, builder [ item1 [ service_id 2, ], ], ]假设你正处在builder.item1这个条目内部想读取外部的client_id$get()相对于当前条目因此$get(client_id)实际等价于查找builder.item1.client_id不存在使用../向上跳一级$get(../client_id)等价于查找builder.client_id$get(../../client_id)等价于查找client_id即顶层字段。特殊情形$get()无参数、$get()或$get(./)始终返回当前 Builder 条目的完整数据数组。十一、Builder 校验规则除 Validation 文档 中列出的通用规则外Builder 还有专属规则。11.1 条目数量校验minItems()/maxItems()use Filament\Forms\Components\Builder; Builder::make(content) -blocks([ // ... ]) -minItems(1) -maxItems(5)minItems()/maxItems()在 Builder 上用于限制整个 Builder 的条目总数二者均可传闭包。底层由CanLimitItemsLengthtrait 实现CanLimitItemsLength.php设置后会自动追加array规则min:N/max:N到校验规则集。另外Builder 在脱水校验时还会强制要求每个条目的type字段必填{$statePath}.*.type [required]见 Builder.php#L1238-L1243。区分两者Builder::maxItems()限制条目总数Block::maxItems()限制同一块类型的出现次数。测试用例可佐证 Builder 的校验与状态处理行为例如 BuilderTest.php 中验证了fillForm()后assertSchemaStateSet()能完整还原[type ..., data [...]]结构以及块内字段使用distinct()校验时重复值会精确地报在builder.0.data.foo/builder.1.data.foo这类路径上。十二、定制 Builder 条目操作ActionsBuilder 内部的每个按钮都是 Filament 的 Action 对象可以通过「操作注册方法」传入闭包进行定制。闭包接收$action对象进而使用 Actions 文档 中的全部定制能力。可定制的操作方法如下方法作用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), )12.1 用模态框确认操作可以对支持网络请求的操作使用requiresConfirmation()弹出确认模态框并可结合 Actions Modals 文档 中的任意模态定制方法use Filament\Actions\Action; use Filament\Forms\Components\Builder; Builder::make(content) -blocks([ // ... ]) -deleteAction( fn (Action $action) $action-requiresConfirmation(), )限制说明addAction()、addBetweenAction()、collapseAction()、collapseAllAction()、expandAction()、expandAllAction()和reorderAction()不支持确认模态框——因为这些按钮的点击不会发出展示模态框所需的网络请求。12.2 在条目头部添加自定义操作extraItemActions()允许你向每个 Builder 条目的头部追加自定义 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]是当前 Builder 条目的 IDgetItemState()返回该条目的已校验数据若条目校验失败动作会被取消并在表单中为该条目显示错误信息若想获取未校验的原始数据改用$component-getRawItemState($arguments[item])。如果要对整个 Builder 的原始数据进行增删改可以先用$component-getState()取回全部数据修改后用$component-state($state)写回use Illuminate\Support\Str; // 获取整个 Builder 的原始数据 $state $component-getState(); // 新增一个条目以随机 UUID 作为 key $state[Str::uuid()] [ type contactDetails, data [ email auth()-user()-email, ], ]; // 写回 Builder $component-state($state);源码对应getItemState()内部调用getChildSchema($key)-getState(shouldCallHooksBefore: false)getRawItemState()则调用getStateSnapshot()Builder.php#L1213-L1224。从registerActions()Builder.php#L125-L138可以看到 Builder 内置注册了 add、addBetween、clone、collapse、collapseAll、delete、edit、expand、expandAll、moveDown、moveUp、reorder 共 12 个动作。十三、文档演示项目中的真实用法本仓库的文档演示应用在 BuilderSchema.php 中几乎覆盖了本文讲到的全部特性基础 Builder、基于内容动态标签labelledBuilder、块图标builderIcons、头部图标builderBlockIcons、添加按钮对齐builderAddActionAlignment、块预览builderBlockPreviews、按钮排序builderReorderableWithButtons、可折叠collapsibleBuilder、默认折叠collapsedBuilder、可克隆cloneableBuilder以及多列块选择器builderBlockPickerColumns。当你需要对照某一特性的完整可运行示例时可以直接翻阅该文件。总结Filament Forms 的 Builder 组件以「多类型、可排序、可嵌套的表单块容器」为设计核心是搭建内容块式编辑器CMS 页面、营销落地页、动态表单模板的高效基础设施。其关键能力可归纳为Block 多 schemablocks()Block::make()定义异构块结构数据以JSON列 arraycast 存储展示控制动态标签、图标、编号、只读/交互预览、折叠与延迟加载交互控制添加含条目间插入、删除、拖拽/按钮排序、克隆以及addable()/deletable()/reorderable()等细粒度开关选择器与约束blockPickerColumns()/blockPickerWidth()布局、Block::maxItems()单块次数限制、minItems()/maxItems()总量校验作用域与扩展$get(../x)跨级取值的路径语义、基于 Action 的 11 种可定制内置操作与extraItemActions()自定义扩展。配合 Forms 概览 与 Schemas 文档 阅读可以进一步掌握它在大型表单、动态页面编辑器中的完整用法。【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价