管理关联
选择合适的工具
Filament 提供了多种在应用中管理管理的方法。选择哪个方式取决于你管理的关联类型和你希望的 UI 界面。
关联管理器 - 资源表单下方的交互式表格
NOTE
该方式兼容 HasMany、HasManyThrough、BelongsToMany、MorphMany 和 MorphToMany 关联。
关联管理器是允许管理员在不离开资源的编辑页或查看页的情况下,列出、创建、编辑、删除、关联、取消关联、附加、分离关联记录的互动式表格。
Select & checkbox list - 从现有记录中选择或者创建新记录
NOTE
该方式兼容 BelongsTo、MorphTo 和 BelongsToMany 关联。
使用 Select,用户可以从已有记录清单中进行选择。你页可以添加一个允许用户在模态框中创建新纪录的按钮,而无需离开页面。
当在 Select 中使用 BelongsToMany 关联时,你可以选择多个选项。记录会在你提交表单时,自动添加到中间表中。你也可以使用 Checkbox List替换多选下拉框。这两个组件的工作原理相同。
Repeater - 在表单中 CRUD 多个关联纪律
NOTE
该方式兼容 HasMany 和 MorphMany 关联。
Repeater是标准的表单组件,可以无限渲染一组可重复的字段。它们可以钩入到关联中,因此记录会自动从相关表中读取、创建、更新和删除。它们位于主表单 Schema 中,可以在资源页面中使用,也可以在嵌套到操作模态框中。
就用户体验角度而言,该方案只适合于关联模型中只有少数几个字段。否则,表单可能会变得非常长。
布局表单组件 - 保存表单字段到单个关联中
NOTE
该方式兼容 BelongsTo、HasOne 和 MorphOne 关联。
所有布局表单组件(Grid、Section、Fieldset等)都由一个 relationship() 方法。当你使用这一方式,所有布局组件类的字段都将 保存到关联模型而非所有者(owner)模型中:
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Fieldset;
Fieldset::make('Metadata')
->relationship('metadata')
->schema([
TextInput::make('title'),
Textarea::make('description'),
FileUpload::make('image'),
])
本例中,title、description 和 image 自动从 metadata 关联中自动加载,并且在表单提交时再次保存。如果该 metadata 记录不存在,它将自动创建。
该特性在表单文档由更详尽深入的解释。请访问了解更多如何使用的信息。
创建关联管理器
要创建关联管理器,你可以使用 make:filament-relation-manager 命令:
php artisan make:filament-relation-manager CategoryResource posts title
CategoryResource是所有者(父级)模型的资源类的名称。posts是你想要管理的关联的名称。title是你用以识别文章(post)的属性名。
这将创建一个 CategoryResource/RelationManagers/PostsRelationManager.php 文件。它包含一个你可以定义资源管理器的表单和表格的类:
use Filament\Forms;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Table;
public function form(Schema $schema): Schema
{
return $schema
->components([
Forms\Components\TextInput::make('title')->required(),
// ...
]);
}
public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('title'),
// ...
]);
}
你必须在资源的 getRelations() 方法中注册新的关联管理器:
public static function getRelations(): array
{
return [
RelationManagers\PostsRelationManager::class,
];
}
当关联管理器定义了表格和表单后,请访问资源的编辑页或查看页,以查看其实际操作。
只读模式
关联管理器通常显示在资源的“编辑”或“查看”页面上。在“查看”页面上,Filament 将自动隐藏修改关联的所有操作,如创建、编辑和删除。我们称之为“只读模式”,默认情况下,它保留了“查看”页面的只读行为。但是,你可以通过重写关联管理器类上的 isReadOnly() 方法使之始终返回 false 来禁用此行为:
public function isReadOnly(): bool
{
return false;
}
此外,如果你不喜欢该功能,你可以在面板配置中一次性禁用所有关联管理器的只读模式:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->readOnlyRelationManagersOnResourceViewPagesByDefault(false);
}
非常规的反转关联名
对于未遵循 Laravel 命名规范的反转关联,你可以在表格上使用 inverseRelationship() 方法:
use Filament\Tables;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('title'),
// ...
])
->inverseRelationship('section'); // Since the inverse related model is `Category`, this is normally `category`, not `section`.
}
处理软删除
默认情况下,你将无法在关联管理器中与已删除的记录进行交互。如果你想在关联管理器中添加恢复、强制删除和过滤垃圾记录的功能,请在生成关联管理器时使用 --soft deletes 标志:
php artisan make:filament-relation-manager CategoryResource posts title --soft-deletes
你可以在此处查阅更多软删除的信息。
列出关联记录
关联记录会在表格中列出。整个关联管理器都是基于此表格,包含创建、编辑、附加/分离、关联/取消关联和删除记录的操作。
你可以使用表格构造器的所有特性来自定义关联管理器。
使用中间表属性罗列
对于 BelongsToMany 和 MorphToMany 关联,你也可以添加中间表属性。比如你的 UserResource 资源有一个 TeamsRelationManager 关联,你想要添加 role 中间表属性到该表格,你可以使用:
use Filament\Tables;
public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('role'),
]);
}
请确保所有的中间表属性都在关联及反转关联的 withPivot() 方法中列出。
创建关联记录
使用中间表属性创建关联
对于 BelongsToMany 和 MorphToMany 关联,你也可以添加中间表属性。比如你的 UserResource 资源有一个 TeamsRelationManager 关联,你想要添加 role 中间表属性到新建表单中,你可以使用:
use Filament\Forms;
use Filament\Schemas\Schema;
public function form(Schema $schema): Schema
{
return $schema
->components([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('role')->required(),
// ...
]);
}
请确保所有的中间表属性都在关联及反转关联的 withPivot() 方法中列出
自定义 CreateAction
要了解如何自定义 CreateAction,包括操纵单数据、修改通知以及添加生命周期钩子,请查阅 Action 文档。
编辑关联记录
使用中间表属性编辑
对于 BelongsToMany 和 MorphToMany 关联,你也可以添加中间表属性。比如你的 UserResource 资源有一个 TeamsRelationManager 关联,你想要添加 role 中间表属性到编辑表单中,你可以使用:
use Filament\Forms;
use Filament\Schemas\Schema;
public function form(Schema $schema): Schema
{
return $schema
->components([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('role')->required(),
// ...
]);
}
请确保所有的中间表属性都在关联及反转关联的 withPivot() 方法中列出。
自定义 EditAction
要了解如何自定义 EditAction,包括操纵单数据、修改通知以及添加生命周期钩子,请查阅 Action 文档。
附加和分离记录
Filament 可以为 BelongsToMany 和 MorphToMany 关联附加和分离记录。
生成关联管理器时,你可以传入 --attach 标志,以同时添加 AttachAction、DetachAction 和 DetachBulkAction 操作到表格中:
php artisan make:filament-relation-manager CategoryResource posts title --attach
另外,如果你已经生成了资源,你可以只添加这些操作到 $table 中:
use Filament\Actions\AttachAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DetachAction;
use Filament\Actions\DetachBulkAction;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
// ...
])
->headerActions([
// ...
AttachAction::make(),
])
->recordActions([
// ...
DetachAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
// ...
DetachBulkAction::make(),
]),
]);
}
预加载附加模态框选择选项
默认情况下,当你搜索要附加的记录时,选项将通过 AJAX 从数据库加载。如果你希望在表单首次加载时预加载这些选项,可以使用 AttachAction 的 preloadRecordSelect() 方法:
use Filament\Actions\AttachAction;
AttachAction::make()
->preloadRecordSelect()
附加中间属性
当你使用 Attach 附加按钮附加记录时,你可能希望定义一个自定义表单来将中间表属性添加到关联中:
use Filament\Actions\AttachAction;
use Filament\Forms;
AttachAction::make()
->form(fn (AttachAction $action): array => [
$action->getRecordSelect(),
Forms\Components\TextInput::make('role')->required(),
])
此例中,$action->getRecordSelect() 返回 Select 字段以选择要附加的记录。然后,role 文本输入框将保存到中间表的 role 列中。
请确保所有的中间表属性都在关联及反转关联的 withPivot() 方法中列出
限定附加的选项查询范围:
你可能想要对 AttachAction 的选项限定查询范围:
use Filament\Actions\AttachAction;
use Illuminate\Database\Eloquent\Builder;
AttachAction::make()
->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))
跨越多列搜索要附加的选项
默认情况下,要附加的 AttachAction 的选项会在表格的 recordTitleAttribute() 中进行搜索。如果你希望搜索多个字段,可以使用 recordSelectSearchColumns() 方法:
use Filament\Actions\AttachAction;
AttachAction::make()
->recordSelectSearchColumns(['title', 'description'])
附加多个记录
AttachAction 组件的 multiple() 方法允许你选择多个值:
use Filament\Actions\AttachAction;
AttachAction::make()
->multiple()
自定义附加模态框中的 Select 字段
你可以通过将函数传递给 recordSelect() 方法来自定义附加过程中使用的 Select 字段对象:
use Filament\Actions\AttachAction;
use Filament\Forms\Components\Select;
AttachAction::make()
->recordSelect(
fn (Select $select) => $select->placeholder('Select a post'),
)
处理重复项
默认情况下,你不允许多次附加同一条记录。这是因为你必须在中间表上设置一个主键 id 列才能使此功能生效。
请确保 id 属性在关联 和 反向关联的 withPivot() 方法中列出。
最后,请将 allowDuplicates() 方法添加到表中:
public function table(Table $table): Table
{
return $table
->allowDuplicates();
}
提升批量分离操作的性能
默认情况下,DetachBulkAction 会将所有 Eloquent 记录加载到内存中,然后循环遍历并逐条分离。
如果你要分离大量记录,建议你使用 chunkSelectedRecords() 方法一次性提取少量记录。这将减少应用的内存占用:
use Filament\Actions\DetachBulkAction;
DetachBulkAction::make()
->chunkSelectedRecords(250)
Filament 在分离 Eloquent 记 录之前会将其加载到内存中,原因有二:
- 允许在分离之前使用模型策略对集合中的单个记录进行授权(比如,使用
authorizeIndividualRecords('delete'))。 - 确保在分离记录时运行模型事件,例如模型观察器中的
deleting和deleted事件。
如果你不需要单个记录策略授权和模型事件,你可以使用 fetchSelectedRecords(false) 方法,该方法不会在分离记录之前将记录加载到内存中,而是在单个查询中分离它们:
use Filament\Actions\DetachBulkAction;
DetachBulkAction::make()
->fetchSelectedRecords(false)
关联和解除关联记录
Filament 能够关联和解除关联 HasMany 和 MorphMany 关系的记录。
生成关联管理器时,你可以传递 --associate 标志,以便将 AssociateAction、DissociateAction 和 DissociateBulkAction 添加到表中:
php artisan make:filament-relation-manager CategoryResource posts title --associate
或者,如果你已经生成了资源,则只需将操作(Action)添加到 $table 数组中:
use Filament\Actions\AssociateAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DissociateAction;
use Filament\Actions\DissociateBulkAction;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
// ...
])
->headerActions([
// ...
AssociateAction::make(),
])
->recordActions([
// ...
DissociateAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
// ...
DissociateBulkAction::make(),
]),
]);
}
预加载关联模态框 Select 选项
默认情况下,当你索要关联的记录时,选项将通过 AJAX 从数据库加载。如果你希望在表单首次加载时预加载这些选项,可以使用 AssociateAction 的 preloadRecordSelect() 方法:
use Filament\Actions\AssociateAction;
AssociateAction::make()
->preloadRecordSelect()
限定关联选项查询范围:
你可能想要对 AssociateAction 的选项限定查询范围:
use Filament\Actions\AssociateAction;
use Illuminate\Database\Eloquent\Builder;
AssociateAction::make()
->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))
搜索选项跨列关联
默认情况下,AssociateAction 可用的选项将在表格的 recordTitleAttribute() 中搜索。如果你希望跨多列搜索,可以使用 recordSelectSearchColumns() 方法:
use Filament\Actions\AssociateAction;
AssociateAction::make()
->recordSelectSearchColumns(['title', 'description'])
关联多个记录
AssociateAction 组件上的 multiple() 方法允许你选择多个值:
use Filament\Actions\AssociateAction;
AssociateAction::make()
->multiple()
自定义关联模态框中的 Select 字段
你可以通过将函数传递给 recordSelect() 方法来自定义关联过程中使用的 Select 字段对象:
use Filament\Actions\AssociateAction;
use Filament\Forms\Components\Select;
AssociateAction::make()
->recordSelect(
fn (Select $select) => $select->placeholder('Select a post'),
)
、、、
提升批量解除关联操作的性能
默认情况下,DissociateBulkAction 会将所有 Eloquent 记录加载到内存中,然后循环遍历并逐一解除关联。
如果你要解除大量记录,建议使用 chunkSelectedRecords() 方法一次性获取较少数量的记录。这将减少应用的 内存占用:
use Filament\Actions\DissociateBulkAction;
DissociateBulkAction::make()
->chunkSelectedRecords(250)
Filament 在解除关联之前会将 Eloquent 记录加载到内存中,原因有二:
- 允许在解除关联之前使用模型策略对集合中的单个记录进行授权(例如,使用
authorizeIndividualRecords('update'))。 - 确保在解除关联记录时运行模型事件,例如模型观察器中的
updating和updated事件。
如果你不需要单个记录策略授权和模型事件,可以使用 fetchSelectedRecords(false) 方法,该方法不会在解除关联之前将记录加载到内存中,而是在单个查询中解除关联:
use Filament\Actions\DissociateBulkAction;
DissociateBulkAction::make()
->fetchSelectedRecords(false)
查看关联记录
生成关联管理器时,你可以传递 --view 标志来向表格中添加 ViewAction:
php artisan make:filament-relation-manager CategoryResource posts title --view
或者,如果你已经生成了关联管理器,则只需将 ViewAction 添加到 $table->recordActions() 数组中:
use Filament\Actions\ViewAction;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
// ...
])
->recordActions([
ViewAction::make(),
// ...
]);
}
删除关联记录
默认情况下,你将无法在关联管理器中与已删除的记录进行交互。如果你想在关联管理器中添加恢复、强制删除和过滤已删除记录的功能,请在生成关联管理器时使用 --soft-deletes 标志:
php artisan make:filament-relation-manager CategoryResource posts title --soft-deletes
或者,你也可以添加软删除功能到现有的关联管理器中:
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
public function table(Table $table): Table
{
return $table
->modifyQueryUsing(fn (Builder $query) => $query->withoutGlobalScopes([
SoftDeletingScope::class,
]))
->columns([
// ...
])
->filters([
TrashedFilter::make(),
// ...
])
->recordActions([
DeleteAction::make(),
ForceDeleteAction::make(),
RestoreAction::make(),
// ...
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
ForceDeleteBulkAction::make(),
RestoreBulkAction::make(),
// ...
]),
]);
}
自定义 DeleteAction
要了解如何自定义 DeleteAction,包括修改通知和添加生命周期钩子,请查看 Action 文档。