简要:升级到 Filament v2.13.0 后,新生成的后台资源(Resource)和关联管理器(Relation Manager)会带有操作(actions)。这些操作让你可以更容易地对后台面板的一些界面进行定制,而不必重写方法或者属性。现有的资源或关联管理器仍然还是可以像之前那样运行,不受影响。
如果你需要了解为什么我们做这些更新,以及如何在现有资源中添加操作(Action)的,请往下读。这是一篇长博文,需要做好心理准备。理解更新的意图,会有助于更好地了解 Filament 内部不是如何运作的。如果你只对后台软删除(soft deletes)感兴趣,可跳到对应部分。如果你对升级现有项目感兴趣,请跳到本文相应部分。
Filament 中的 Action 是用来在后台面板中运行任务的 PHP 对象。比如,你可以删除 Eloquent 记录,分发队列任务,打开 URL。Action 很强大,可以打开模态框并接收用户输入,当运行时这些输入可以作为运行数据。
Action 有许多不同类型,在 Filament 项目的不同地方使用到了:
表格在每一行的尾部都可以有 Action。默认情况下,后台资源(Resource) 有一个"编辑" 操作链接,指向了资源的编辑页。
表格同时也有头部(Header) Action,展示于表格的上面。关联管理器这样的组件使用,用来渲染“新建”按钮,用以打开模态框创建新的关联记录。
同时,表格也可以由批量操作(bulk action),用来运行任务一次性操作多条记录。资源和关联管理器中有开箱即用的批量操作按钮。
最近,我们也在表单构造器种添加了操作的概念。现在表单组件也可以通过模态框打开它们自己的操作了。下拉列表(Select)字段可以打开“新建选项表单”,用来添加新的选项到下拉列表中,并存储在数据库中。比如,在订单资源中新建用户时,可以因此不离开页面进行操作。
操作的状态 - v2.13.0 之前
此前,所有的 Action 都继承了基础的“Action”类。比如:
use Filament\Tables\Actions\Action;
use Illuminate\Database\Eloquent\Model;
Action::make('edit')
->label('Edit customer')
->url(fn (): string => CustomerResource::getUrl('edit', ['record' => $record]))
->color('primary')
->icon('heroicon-s-pencil')
Action::make('delete')
->label('Delete customer')
->action(fn (Model $record) => $record->delete())
->requiresConfirmation()
->color('danger')
->icon('heroicon-s-trash')
“edit”和“delete”操作都使用 Action 类来创建新的操作。
这也没什么问题,只是我们发现在后台的不同组件中有做了一些重复性工作,比如:
资源中有 “Edit” 操作,用来跳转到资源的编辑页面,或在不离开页面的情况下打开模态框编辑记录。
关联管理器中也有 “Edit” 操作,允许用户和编辑关联记录。
如果用户使用表格构造器创建自定义表格,就需要自己创建“Edit”操作。
如果使用模态框编辑记录,所有这些 Edit 操作都使用了 Filament\Tables\Actions\Action
类,都有同样的标签、同样的颜色、同样的图标以及同样的逻辑,用来加载或者保存数据。
除了重复代码之外,每一处的 “Edit” 操作都复制了翻译字符,也让开发者翻译时增加了额外的开销。
还有内部问题,最终用户的DX 体验也有待优化。比如,要改变 action 的标签,你需要在后台面板页面类中重写方法:
use Filament\Pages\Actions\Action;
protected function getCreateAction(): Action
{
return parent::getCreateAction()->label('New label');
}
此处代码比较简单,它依赖于继承,getCreateAction() 起初来自于哪里可能有点不太清楚。
同时,如果用户要添加自己的操作,体验也不是最佳的。比如,添加新的页面操作大概像这样:
use Filament\Pages\Actions;
protected function getActions(): array
{
return array_merge(parent::getActions(), [
Actions\Action::make('replicate'),
]);
}
还有,使用 prependActions()
和 pushActions()
在资源中添加新的表格操作到已有的 actions 数组中,总还是有点不舒服:
use Filament\Tables;
// Existing table actions: Edit, View
$table
->prependActions([
Tables\Actions\Action::make('replicate'),
])
->pushActions([
Tables\Actions\Action::make('delete'),
])
// New table actions: Replicate, Edit, View, Delete
重构目标
现在我们能已经清楚了当前 Action 结构的问题,是时候理清重构目标了。这些问题可否在不引入破坏性更新之下就能解决?
介绍 - 预制操作类减少代码重复
我(笔者)和 Ryan (开发者之一) 讨论过 “预制”操作这一概念有一段时间了。这个概念很简单 - 让每一个主要的 CRUD 操作都有一个专门的操作类,让同一类型的操作包含共享代码。
2022年4月27日,我们发布了 v2.11.4,引入 “replicate” 操作。这是我们第一次将“预制”操作引入基础代码库。使用起来很简单 - 你可以在任何表格中添加,它会复制一条记录:
use Filament\Tables;
$table
->prependActions([
Tables\Actions\ReplicateAction::make(),
])
这是一个简单的起点,证明了专门操作类的概念能行。之后,我们很快便开始计划到 Filament v3 之时,让所有操作都实现这一特性。这将让我们引入一些很酷的新特性到后台面板中,而原本这些操作需要进行自定义。比如,我们为处理软删除添加了预制操作 - ForceDeleteAction、ForceDeleteBulkAction、RestoreAction 和 RestoreBulkAction。我们甚至可以引入 TrashedFilter,让用户无需创建自己的多租户过滤器,就能在查询中筛查已删除记录。
我也和 Dennis Koch 谈论过此事,他决定了为软删除类和 TranedFilter 提供 PR 支持。他做的很好,也因此启发了我继续重构整个 Action 架构,使之可以使用预制类。我不喜欢在基础代码库中一半使用自定义类、一般使用此结构的想法。
v2.13.0 中,现在我们有了以下 Action 类:
AassociateAction - 关联现有记录到一对多关联中。它打开了一个模态框,其中包含下拉列表,允许用户选择记录进行关联。可用于表格头部和后台面板页面。
AttachAction - 附加现有记录到多对多关联。它打开了一个模态框,其中包含下拉列表,允许用户选择记录进行附加。可用于表格头部和后台面板页面。
CreateAction - 新建记录。它打开一个模态框,其中有一个表单,提交后会保存到数据库中。可选的,你可以指定 URL,让你跳转到新建页面,而不使用模态框表单。可用于表格头部和后台面板页面。
DeleteAction - 从数据库中删除记录。会先在模态框中要求确认。可用在表格行中和后台面板页面中。
DeleteBulkAction - 从数据库中删除多条记录。会先在模态框中要求确认。可用在表格批量操作的下拉菜单中。
DetachAction - 从多对多关联中取消附加记录。会先在模态框中要求确认。可用在表格行中。
DissociateAction - 从一对多关联中移除一条记录。会先在模态框中要求确认。可用在表格行中。
DissociateBulkAction - 从一对多关联中移除记录。会先在模态框中要求确认。可用在表格批量操作的下拉菜单中。
EditAction - 为现有记录更新数据。它打开一个模态框,其中有一个表单,提交后会保存到数据库中。作为可选项,你可以指定 URL,这样你可以不必使用模态框表单,跳转到编辑页面。可用于表格行中和后台面板页面。
ForceDeleteAction - 从数据库中强制删除已经软删除的记录。会先在模态框中要求确认。可用在表格行中和后台面板页面中。
ForceDeleteBulkAction - 从数据库中强制删除多条已经软删除的记录。会先在模态框中要求确认。用在表格批量操作的下拉菜单中。
ReplicateAction - 在数据库中复制记录。也允许用户在模态框中使用表单自定义新记录。会先在模态框中要求确认。可用在表格行中和后台面板页面中。
RestoreAction - 在数据库中恢复一条软删除记录。会先要求在模态框中确认。可用在表格行中和后台面板页面中。
RestoreBulkAction - 在数据库中恢复多条软删除记录。会先要求在模态框中确认。用在表格批量操作的下拉菜单中。
ViewAcction - 使用模态框表单显示记录。所有表单中的字段禁用,不能编辑。作为可选项,你可以指定 URL,这样你可以不必使用模态框表单,直接跳转到查看页面。可用于表格行中和后台面板页面。
已经很多了。不过,后台面板有很多功能,我希望可以全部覆盖到。
我花了一些时间来创建这些操作类,然后使用它们重构了现有后台面板。
在表格构造器中使用新的操作类
在减少 Filament 基础代码库内部重复代码的同时,预制操作为表格构造器的用户解锁了一系列的新的开发体验上的优化。现在你不用再自己创建 CRUD 操作了,只需这样:
use Filament\Tables;
protected function getTableHeaderActions(): array
{
return [
Tables\Actions\CreateAction::make()->form([...]),
];
}
protected function getTableActions(): array
{
return [
Tables\Actions\ViewAction::make()->form([...]),
Tables\Actions\EditAction::make()->form([...]),
Tables\Actions\DeleteAction::make(),
];
}
protected function getTableBulkActions(): array
{
return [
Tables\Actions\DeleteBulkAction::make(),
];
}
这会在后续的文档中加以说明。
后台资源和关系管理器的更新
现在处理最后两个目标 - 让想要在后台面板中自定义操作的用户改进开发体验。
新特性:代码中的操作
先提醒一下,之前在后台面板表格中添加新操作,需要使用 prependActions()
或 pushActions()
:
use Filament\Tables;
$table
->prependActions([
Tables\Actions\ReplicateAction::make(),
])
现在,我么在生成的资源和关联管理器代码引入了操作类:
use Filament\Tables;
$table
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
])
因此你可以这样添加新的操作:
use Filament\Tables;
$table
->actions([
Tables\Actions\ReplicateAction::make(),
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
])
之前添加操作到资源中是这样的:
use Filament\Pages\Actions;
protected function getActions(): array
{
return array_merge(parent::getActions(), [
Actions\ReplicateAction::make(),
]);
}
现在,我们在资源页面的代码中引入了操作类:
use Filament\Pages\Actions;
protected function getActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
因此,你可以这样添加新操作:
use Filament\Pages\Actions;
protected function getActions(): array
{
return [
Actions\DeleteAction::make(),
Actions\ReplicateAction::make(),
];
}
这样的冗余,可以让 Filament 代码看起来更加清晰。
新特性:通过链式方法自定义操作
现在你可以使用链式方法,更容易地对操作进行自定义。不用再重写方法或属性:
use App\Models\User;
use Filament\Pages\Actions\CreateAction;
use Illuminate\Auth\Events;
protected function getActions(): array
{
return [
CreateAction::make()
->label('Register user')
->modalHeading('Sign up user')
->after(fn (User $record) => event(new Registered($record))),
];
}
新特性:合并关联管理器
之前,你需要根据关联使用不同的命令来生成资源管理器:
php artisan make:filament-has-many ...
php artisan make:filament-belongs-to-many ...
php artisan make:filament-morph-many ...
使用不同命令,意味着你会生成不同的关联管理器类 - HasManyRelationManager
, BelongsToManyRelationManager
, MorphManyRelationManager
。不同的关联表格有不同的操作,比如,BelongsToManyRelationManager
有 attach / detach 操作而 HasManyRelationManager
没有。
再 v2.13.0 中,你只需要一个命令就能生成关联管理器了:
php artisan make:filament-relation-manager ...
这个命令会生成一个简单继承 RelationManager 的类,用于所有类型的关联。
要添加 attach / detach 或 associate / dissociate 操作,可以使用 --attach 或 --associate:
php artisan make:filament-relation-manager ... --attach
php artisan make:filament-relation-manager ... --associate
自定义附加操作
之前的 Filament 文档中的描述,你需要再关联管理器类中使用像 $shouldPreloadAttachActionRecordSelectOptions
这样的属性来预加载下拉列表选项,用来附加操作。现在已经将其移到 preloadRecordSelect()
方法中:
use Filament\Tables\Actions\AttachAction;
AttachAction::make()->preloadRecordSelect()
你可能想要自定义表单添加中间表属性到关联中。现在可以在 form()
方法中实现了,替代原来在关联管理器中重写 attachForm()
方法:
use Filament\Forms;
use Filament\Tables\Actions\AttachAction;
AttachAction::make()
->form(fn (AttachAction $action): array => [
$action->getRecordSelect(),
Forms\Components\TextInput::make('role')->required(),
])
此例中, $action->getRecordSelect()
输出了下拉列表,用以选择附加。之后角色的文本输入内容会报错到中间表的角色字段中。
新特性:部分简易(模态)资源
简易资源指的是只有一个页面 - 管理页 - 的资源。它在模态框中处理了所有操作。
生成简易资源的方式不变,现在 table()
方法中包含了所有预制的操作。
不过,你现在可以让资源部分“简单”。比如,你想让用户用模态框创建新记录,但是更新记录时使用编辑页面。这种情况下,你可以只删除新建(Create) 页面类并在资源 getPages()
方法中移除, Filament 会检测到没有新建页,不过列表页中还是有 CreateAction
在,就会在模态框中打开新建表单!
此外,你也可以将简易资源表格中的 "delete" 操作“偷”到正常的资源中。你可以在资源文件中这样添加 DeleteAction
:
use Filament\Tables;
$table
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
新特性:原生支持软删除
make:filament-resource
和 make:filament-relation-manager
命令中,已经增加了 --soft-deletes
标志:
php artisan make:filament-resource ... --soft-deletes
php artisan make:filament-relation-manager ... --soft-deletes
这将为资源或关联管理器文件自动配置软删除操作和 TrashedFilter:
use Filament\Tables;
$table
->columns([
//
])
->filters([
Tables\Filters\TrashedFilter::make(),
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
Tables\Actions\RestoreBulkAction::make(),
Tables\Actions\ForceDeleteBulkAction::make(),
]);
use Filament\Pages\Actions;
protected function getActions(): array
{
return [
Actions\DeleteAction::make(),
Actions\ForceDeleteAction::make(),
Actions\RestoreAction::make(),
];
}
也会同时在资源中删除 SoftDeletingScope,这样你就可以不受限制地访问软删除记录了。
"升级"后台面板
首先,要确保你使用的是 Filament v2.13.0 版本。如果你使用了 Filament 官方插件,也要同时确保用的是 v2.13.0 版本。
资源
在资源中,添加操作到 $table->actions()
和 $table->bulkActions()
。如果不需要可排除 ViewAction
和 DeleteAction
:
use Filament\Tables;
$table
->columns([
//
])
->actions([
Tables\Actions\ViewAction::make(),
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
在列表页中:
use Filament\Pages\Actions;
protected function getActions(): array
{
return [
Actions\CreateAction::make(),
];
}
在编辑页类中(如果没有查看页可以删除 ViewAction):
On the Edit page class (you might want to delete the ViewAction if you don't have a View page):
use Filament\Pages\Actions;
protected function getActions(): array
{
return [
Actions\ViewAction::make(),
Actions\DeleteAction::make(),
];
}
在查看页类中(如果无需没有编辑页可以删除 EditAction):
use Filament\Pages\Actions;
protected function getActions(): array
{
return [
Actions\EditAction::make(),
];
}
简易(模态)资源
使用新的 action 结构后,自定义简易资源的处理也改变了。我们在文档中对新的自定义选项进行了说明。可查阅文档。
关联管理器R
首先,现在关联管理器只继承了一个类 - RelationManager:
use Filament\Resources\RelationManagers\RelationManager;
class PostsRelationManager extends RelationManager
{
// ...
}
现在,添加头部操作,行内操作及批量操作到 $table:
use Filament\Tables;
$table
->columns([
//
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make(),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\DeleteBulkAction::make(),
]);
如果有多对多关联管理器,你可能需要 attach/detach 操作:
use Filament\Tables;
$table
->headerActions([
// ...
Tables\Actions\AttachAction::make(),
])
->actions([
// ...
Tables\Actions\DetachAction::make(),
])
->bulkActions([
// ...
Tables\Actions\DetachBulkAction::make(),
]);
如果是一对多关联管理器,你可能需要 associate/dissociate 操作:
use Filament\Tables;
$table
->headerActions([
// ...
Tables\Actions\AssociateAction::make(),
])
->actions([
// ...
Tables\Actions\DissociateAction::make(),
])
->bulkActions([
// ...
Tables\Actions\DissociateBulkAction::make(),
]);
关联管理器自定义处理随着使用新的Action结构而改变。请查实类中的属性或者方法是否已被弃用。