Filament CreateAction 完整实战指南:用模态框表单创建 Eloquent 记录的官方实现与源码剖析
【免费下载链接】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 内置的CreateAction是构建后台「新建记录」流程的高效组件:点击触发按钮即弹出包含表单的模态框,提交后自动完成校验、落库、通知与跳转。本文以官方文档 packages/actions/docs/04-create.md 为骨架,结合CreateAction源码与测试,深入讲解数据预处理、创建流程定制、生命周期钩子、向导式多步表单以及「创建并继续创建」等全部能力,读完即可在表格 Action、表单 Action 与资源页面中直接落地使用。
一、快速上手:一个能落库的创建弹窗
在 Filament 中,任何「点击按钮 → 弹出表单 → 校验并写入数据库」的需求,都可以通过Filament\Actions\CreateAction一行式链式调用完成:
use Filament\Actions\CreateAction; use Filament\Forms\Components\TextInput; CreateAction::make() ->schema([ TextInput::make('title') ->required() ->maxLength(255), // ... 更多字段 ])当触发按钮被点击时,Filament 会打开一个模态框,模态框内渲染schema()中定义的字段;用户填写并提交后,数据经过表单校验被保存为一条新的 Eloquent 记录。
从源码来看,CreateAction继承自Action,并定义了默认行为(见 packages/actions/src/CreateAction.php):
- 默认名称
getDefaultName()返回'create'; - 默认标签来自语言包
filament-actions::create.single.label; - 模态框标题
modalHeading、提交按钮文案均使用内置翻译键; - 默认开启「创建另一个」扩展按钮(
canCreateAnother()默认为true),并预置成功通知标题。
测试用例也验证了这一基础契约,见 tests/src/Actions/CreateActionTest.php:在关系管理器中渲染CreateAction、挂载模态框、填充表单、调用 Action 后断言数据库出现对应记录,并断言弹出成功通知。
二、保存前修改表单数据:mutateDataUsing()
有些场景需要在数据落库前进行加工,例如自动注入当前登录用户 ID、生成时间戳或对字段做归一化。此时使用mutateDataUsing(),它接收$data数组并返回修改后的数组:
use Filament\Actions\CreateAction; CreateAction::make() ->mutateDataUsing(function (array $data): array { $data['user_id'] = auth()->id(); return $data; })该方法不仅支持静态闭包,还可以注入 Action 自身的各种工具参数(如$action、$livewire、$schema等),用于更精细的动态处理。
三、完全接管创建过程:using()
如果默认的「实例化模型 → fill → save」流程无法满足需求(例如调用第三方服务、写入额外表、调用模型自定义方法),可以用using()完全接管记录的创建逻辑:
use Filament\Actions\CreateAction; use Illuminate\Database\Eloquent\Model; CreateAction::make() ->using(function (array $data, string $model): Model { return $model::create($data); })其中$model是模型类名字符串,你也可以在闭包内硬编码自己的模型类。闭包必须返回一个 Eloquent 模型实例。
从源码看,using()定义于CanCustomizeProcesstrait(packages/actions/src/Concerns/CanCustomizeProcess.php):它把闭包存为$this->using,而process()方法执行evaluate($this->using ?? $default, $parameters)——即「有自定义则用自定义,否则用默认流程」。
CreateAction的默认流程(见 CreateAction.php)相当精细:
- 解析
getRelationship(),判断是否在关联关系上下文中创建; - 若是
BelongsToMany关系,则把枢轴(pivot)列从$data中分离出来(Arr::only/Arr::except); - 若 Livewire 组件启用了可翻译内容驱动(
makeFilamentTranslatableContentDriver()),通过驱动创建记录,否则new $model并fill($data); - 无关联或
HasOneOrManyThrough关系时直接$record->save(); - 普通关联关系通过
$relationship->save($record)保存(自动维护外键); BelongsToMany关系通过$relationship->save($record, $pivotData)同时写入枢轴表。
这也解释了为什么在关系管理器中创建的记录能自动挂到父记录下——测试 attaches created record to relationship 专门验证了这一点。
四、创建成功后的跳转:successRedirectUrl()
默认情况下,创建成功后 Filament 会停留在当前页面。你可以通过successRedirectUrl()指定跳转目标:
use Filament\Actions\CreateAction; CreateAction::make() ->successRedirectUrl(route('posts.list'))如果需要基于刚创建的记录跳转(例如跳到编辑页),闭包可以注入$record参数:
use Filament\Actions\CreateAction; use Illuminate\Database\Eloquent\Model; CreateAction::make() ->successRedirectUrl(fn (Model $record): string => route('posts.edit', [ 'post' => $record, ]))其底层实现在 packages/actions/src/Concerns/CanRedirect.php:dispatchSuccessRedirect()会先evaluate自定义 URL,若为空则回退到 Livewire 组件的getDefaultActionSuccessRedirectUrl();redirect()方法还会根据是否启用 SPA 模式自动决定是否使用navigate进行前端路由跳转。
五、定制成功通知
记录创建成功后,Filament 会向用户派发一条成功通知。默认标题来自语言包,你可以覆盖:
use Filament\Actions\CreateAction; CreateAction::make() ->successNotificationTitle('User registered')标题也支持闭包动态计算。若想定制整条通知(类型、标题、正文等),使用successNotification():
use Filament\Actions\CreateAction; use Filament\Notifications\Notification; CreateAction::make() ->successNotification( Notification::make() ->success() ->title('User registered') ->body('The user has been created successfully.'), )successNotification()同样接受闭包,闭包内可注入默认的$notification对象作为定制起点。完全禁用通知则传入null:
use Filament\Actions\CreateAction; CreateAction::make() ->successNotification(null)从 CanNotify 源码可以看出:传入null时isSuccessNotificationDisabled会被置为true,sendSuccessNotification()直接短路返回;发送前还会检查通知标题是否为空(filled($notification?->getTitle())),空标题不发送。注意该 trait 同时提供了failureNotification()、unauthorizedNotification()、rateLimitedNotification()等配套方法,但CreateAction主要使用成功通知。
六、生命周期钩子:在创建流程的关键节点注入代码
Filament 为创建流程提供了 6 个生命周期钩子,覆盖「表单填充默认值 → 校验 → 保存」的完整链路:
use Filament\Actions\CreateAction; CreateAction::make() ->beforeFormFilled(function () { // 表单字段填充默认值之前执行 }) ->afterFormFilled(function () { // 表单字段填充默认值之后执行 }) ->beforeFormValidated(function () { // 表单提交、字段校验之前执行 }) ->afterFormValidated(function () { // 表单字段校验通过之后执行 }) ->before(function () { // 表单字段保存到数据库之前执行 }) ->after(function () { // 表单字段保存到数据库之后执行 })钩子定义与调用在 packages/actions/src/Concerns/HasLifecycleHooks.php 中:每个before*/after*方法只是把闭包存入对应属性,callBefore()/callAfter()等call*方法负责evaluate执行。值得注意的实现细节是,callBefore()会先派发ActionCalling事件,而callAfter()在闭包执行完毕后(finally块中)派发ActionCalled事件——这意味着钩子与事件系统是联动的,你可以在全局监听这些事件对 Action 调用做统一埋点或审计。
七、中断或取消创建流程:halt() 与 cancel()
你可以在任何生命周期钩子或数据处理方法中调用$action->halt()中断整个创建流程。一个典型场景是订阅校验:
use App\Models\Post; use Filament\Actions\Action; use Filament\Actions\CreateAction; use Filament\Notifications\Notification; CreateAction::make() ->before(function (CreateAction $action, Post $record) { if (! $record->team->subscribed()) { Notification::make() ->warning() ->title('You don\'t have an active subscription!') ->body('Choose a plan to continue.') ->persistent() ->actions([ Action::make('subscribe') ->button() ->url(route('subscribe'), shouldOpenInNewTab: true), ]) ->send(); $action->halt(); } })如果你希望模态框也随之关闭,则改用cancel()完全取消 Action:
$action->cancel();两者的底层实现(packages/actions/src/Action.php)都是抛出专用异常来中断执行流:cancel()抛出Cancel,halt()抛出Halt,并且都支持$shouldRollBackDatabaseTransaction参数——结合CanUseDatabaseTransactions使用时,可以回滚当前数据库事务。区别在于Cancel会让整个 Action 流程终止且模态框关闭,而Halt仅中断流程、保留模态框状态。halt()还有一个已弃用的别名hold()。
八、把创建流程变成多步向导:steps()
当新建表单字段较多时,可以将其拆分为多步向导(Wizard)。不再使用schema(),而是通过steps()传入一组Step对象:
use Filament\Actions\CreateAction; use Filament\Forms\Components\MarkdownEditor; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; use Filament\Schemas\Components\Wizard\Step; CreateAction::make() ->steps([ Step::make('Name') ->description('Give the category a unique name') ->schema([ TextInput::make('name') ->required() ->live() ->afterStateUpdated(fn ($state, callable $set) => $set('slug', Str::slug($state))), TextInput::make('slug') ->disabled() ->required() ->unique(Category::class, 'slug'), ]) ->columns(2), Step::make('Description') ->description('Add some extra details') ->schema([ MarkdownEditor::make('description'), ]), Step::make('Visibility') ->description('Control who can view it') ->schema([ Toggle::make('is_visible') ->label('Visible to customers.') ->default(true), ]), ])每个Step都可以独立设置描述、字段 schema 与栅格列数(columns(2))。Step类位于 schemas 包的Filament\Schemas\Components\Wizard\Step,与表单/表格中使用的向导组件同源。文档同时提醒:这种「创建即向导」的方式只影响创建 Action 本身,编辑(Edit)仍使用资源类中定义的表单。
如果希望所有步骤都可自由跳转(不再强制按顺序填写),追加skippableSteps():
use Filament\Actions\CreateAction; CreateAction::make() ->steps([ // ... ]) ->skippableSteps()九、「创建并继续创建」:提升录入效率的进阶能力
CreateAction 的模态框底部默认带有「创建另一个」按钮,允许用户连续录入多条记录,非常适合后台批量录入场景。
9.1 定制「创建另一个」按钮
通过createAnotherAction()传入一个返回 Action 的闭包来修改按钮,闭包接收$action参数,所有可用的触发按钮定制方法都可使用:
use Filament\Actions\CreateAction; CreateAction::make() ->createAnotherAction(fn (Action $action): Action => $action->label('Custom create another label'))源码(CreateAction.php)中getCreateAnotherAction()通过makeModalSubmitAction('createAnother', arguments: ['another' => true])生成默认按钮,再交由modifyCreateAnotherActionUsing闭包改写;测试 can modify the create another action 验证了将标签改为'Save & New'后getLabel()返回正确结果。
9.2 移除「创建另一个」按钮
use Filament\Actions\CreateAction; CreateAction::make() ->createAnother(false)createAnother()接受布尔值或闭包(bool | Closure),默认true。与之等价的旧方法disableCreateAnother()已标记为弃用,官方推荐统一使用createAnother(false)。相关测试见 tests/src/Actions/CreateActionTest.php,覆盖了布尔值与闭包两种赋值方式。
9.3 保留表单数据:preserveFormDataWhenCreatingAnother()
默认情况下,「创建另一个」会清空整个表单让用户重新开始。若想保留部分字段(例如is_admin、organization这类每条记录都相同的值),传入字段名数组:
use Filament\Actions\CreateAction; CreateAction::make() ->preserveFormDataWhenCreatingAnother(['is_admin', 'organization'])也可以传入一个函数,从$data中挑选要保留的数据:
use Filament\Actions\CreateAction; use Illuminate\Support\Arr; CreateAction::make() ->preserveFormDataWhenCreatingAnother(fn (array $data): array => Arr::only($data, ['is_admin', 'organization']))甚至返回整个$data来保留所有数据:
use Filament\Actions\CreateAction; CreateAction::make() ->preserveFormDataWhenCreatingAnother(fn (array $data): array => $data)其内部实现(CreateAction.php)是:传入数组时自动包装为Arr::only($data, $fields)的闭包;传入闭包时原样保存。测试覆盖了保留标量字段与保留 Repeater 复杂结构两种场景(tests/src/Actions/CreateActionTest.php),并验证了「创建另一个」后autofocus()字段会通过reset-schema-component-state事件重新聚焦(L130-L142)。
9.4 创建另一个背后的完整流程
结合 CreateAction.php 的action()实现,可以还原「创建另一个」的完整链路:
- 检测参数
$arguments['another'],若为true先取出待保留的原始状态$preserveRawState; - 执行创建流程(含
mutateDataUsing与using); - 记录创建成功后:发送成功通知 → 重置
$record为null→ 用空模型重建 schema →schema->fill()重新填充默认值 → 将保留的数据合并回rawState→ 派发reset-schema-component-state客户端事件; - 调用
$this->halt()结束本轮(模态框保持打开),等待下一次提交。
此外forceRenderAfterCreateAnother()可以强制 Livewire 组件在「创建另一个」后重新渲染,适用于某些需要刷新列表或关联数据的场景(默认关闭,测试见 L257-L265)。
十、小结与实战建议
至此,FilamentCreateAction的核心能力已全部覆盖:模态框 + 表单的快速创建、mutateDataUsing()数据预处理、using()全接管、successRedirectUrl()智能跳转、成功通知定制、6 个生命周期钩子、halt()/cancel()流程控制、多步向导,以及「创建并继续创建」的完整增强能力。
根据源码与测试,几点实战建议:
- 关系上下文自动处理:在 Relation Manager 或带
relationship()的 Action 中,默认创建流程会自动维护外键与枢轴表,无需手写关联保存逻辑; - 钩子与事件联动:
before/after钩子会同步派发ActionCalling/ActionCalled事件,可据此做全局审计; - 事务一致性:
halt()/cancel()支持$shouldRollBackDatabaseTransaction参数,在需要原子性的流程中结合数据库事务使用; - 向导与资源表单分离:创建向导只影响 CreateAction 本身,编辑表单仍由资源类定义,两者互不干扰。
相关参考:Actions 总览、编辑 Action、视图 Action、Form 组件文档、Schema / 布局组件。
【免费下载链接】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),仅供参考