管理关联
选择合适的工具
Filament 提供了许多方法用来在应用中管理关联。该 使用哪个特性取决于你管理的关联类型,以及你想要使用哪种 UI。
关联管理器 - 资源表单下的交互式表格
兼容
HasMany
、HasManyThrough
、BelongsToMany
、MorphMany
和MorphToMany
关联。
关联管理器是一个允许管理员在不离开资源编辑页或者查看页的情况下,罗列、创建、编辑、删除、关联和取消关联相关记录的交互式表格。
Select & checkbox list - 从现有记录中选择或创建新记录
兼容
BelongsTo
、MorphTo
和BelongsToMany
关联。
使用 select,用户可以从现有记录列表中选择。你也可以添加按钮,使之能让你在模态框中添加新记录,而不必离开当前页面。
当在 Select 中使用 BelongsToMany
关联时,你将可以选中多个选项。记录会在你提交表单时,自动添加到透视表中。如果需要,你可以将多选下拉列表换成简单的复选项列表。两个组件的工作原理是一样的。
Repeaters - 在所有者的表单内 CRUD 多个关联记录
兼容
HasMany
和MorphMany
关联。
Repeaters是标准的表单组件,可以无限地渲染一套可重复的字段。它们可以和一个关联挂钩,这样记录旧可以从关联表中自动读取、创建、更新及删除。它们位于主表单 schema 的内部,可用在资源页的内部。也可以嵌套在 Action 模态框中。
以用户体验来说,该方案只适用于关联模型的字段有限。否则,表单可能会十分长。
布局表单组件 - 将表单字段保存到单个关联中
兼容
BelongsTo
、HasOne
和MorphOne
关联。
所有的布局组件(Grid、Section, Fieldset 等)都有一个 relationship()
方法。使用该方法时,在该布局内的所有字段会被保存到关联模型中,而非所有者的模型:
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
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\Forms\Form;
use Filament\Tables;
use Filament\Tables\Table;
public function form(Form $form): Form
{
return $form
->schema([
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,
];
}
当完成资源管理器的表格和表单定义后,可以访问资源的编辑页或查看页,在 Action 中查看。
只读模式
关联管理器通常展示在资源的编辑或者查看页面。在查看页面上,Filament 自动隐藏所有修改关联的所有 Action,如创建、编辑及删除。我们称之为"只读模式",因为默认情况下查看页面保留了只读行为。不过,你可以在关联管理器类上重写 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
查看更多软删除的相关内容,请点击此处。
展示关联记录
关联记录会在一个表格中展示。整个关联管理器都是基于这个表格,包括新建、编辑、附加/分离记录、关联/取消关联和删除记录等操作(Action)。
按照列表页中的文档,你可以在关联管理器类中使用所有的自定义功能:
此外,你也可以使用表格构造器的其他所有特性,来自定义关联管理器。
使用中间属性展示列表
Listing with pivot attributes
对于 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\Forms\Form;
public function form(Form $form): Form
{
return $form
->schema([
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\Forms\Form;
public function form(Form $form): Form
{
return $form
->schema([
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
数组添加到 actions 中:
use Filament\Tables;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
// ...
])
->headerActions([
// ...
Tables\Actions\AttachAction::make(),
])
->actions([
// ...
Tables\Actions\DetachAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
// ...
Tables\Actions\DetachBulkAction::make(),
]),
]);
}
预加载附加模态框的下拉列表选项
默认情况下,当你搜索记录进行附加时,选项会通过 AJAX 从数据库中加载。如果你希望表单加载时就直接预载这些选项,你可以使用 AttachAction
的 preloadRecordSelect()
方法:
use Filament\Tables\Actions\AttachAction;
AttachAction::make()
->preloadRecordSelect()
使用中间属性附加记录
当你使用 附加(Attach)
按钮来附加记录时,你可能希望使用自定义表单来将中间属性添加到关联中:
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()
返回下拉列表字段,用以选择记录附加。role
文本输入框被保存到中间表的 role
字段中。
请确保所有中间表的字段都在 关联 和 反向关联 的 withPivot
方法中罗列出来。
设置附加选项的范围
你可能想要对 AttachAction
中可用的选项进行限制:
use Filament\Tables\Actions\AttachAction;
use Illuminate\Database\Eloquent\Builder;
AttachAction::make()
->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))
通过多个字段搜索选项
默认情况下,AttachAction
的可用选项会在表格的 recordTitleAttribute()
中搜索。如果你想搜索多个字段,你可以使用 recordSelectSearchColumns()
方法:
use Filament\Tables\Actions\AttachAction;
AttachAction::make()
->recordSelectSearchColumns(['title', 'description'])
自定义附加模态框的 Select 字段
你可以传入一个函数到 recordSelect()
方法,自定义附加之中使用的 Select 字段对象:
use Filament\Forms\Components\Select;
use Filament\Tables\Actions\AttachAction;
AttachAction::make()
->recordSelect(
fn (Select $select) => $select->placeholder('Select a post'),
)
处理重复数据
默认情况下,你不能多次附加同一个记录。要使之可行,你需要先在中间表设置一个主键 id
字段。
同时,要确保 关联 和 反向关联 的 withPivot()
方法中使用了 id
属性。
最后,请在关联管理器中添加 $allowsDuplicates
属性:
protected bool $allowsDuplicates = true;
关联和取消关联记录
Filament 可以为 HasMany
和 MorphMany
关系关联和取消关联记录。
在生成关联管理器时,你可以传入 --associate
标志,这样就能将 AssociateAction
、DissociateAction
和 DissociateBulkAction
同时添加到表格中:
php artisan make:filament-relation-manager CategoryResource posts title --associate
此外,如果你已经生成了资源,你也可以将这些 actions 添加到 $table
数组中:
use Filament\Tables;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
// ...
])
->headerActions([
// ...
Tables\Actions\AssociateAction::make(),
])
->actions([
// ...
Tables\Actions\DissociateAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
// ...
Tables\Actions\DissociateBulkAction::make(),
]),
]);
}
预加载关联模态框的 Select 选项
默认情况下,由于你通过搜索记录关联,选项会通过 AJAX 从数据库中加载。如果你希望在表单加载时就预加载这些选项,可以使用 AssociateAction
的 preloadRecordSelect()
方法:
use Filament\Tables\Actions\AssociateAction;
AssociateAction::make()
->preloadRecordSelect()
设置选项范围以供关联
你可能想要对 AssociateAction
中可用的选项进行限制:
use Filament\Tables\Actions\AssociateAction;
use Illuminate\Database\Eloquent\Builder;
AssociateAction::make()
->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))
通过多个字段搜索关联选项
默认情况下,AssociateAction
的可用选项会在表格的 recordTitleAttribute()
中搜索。如果你想搜索多个字段,你可以使用 recordSelectSearchColumns()
方法:
use Filament\Tables\Actions\AssociateAction;
AssociateAction::make()
->recordSelectSearchColumns(['title', 'description'])
自定义关联模态框的 Select 字段
你可以传入一个函数到 recordSelect()
方法,自定义关联期间使用的 Select 字段对象:
use Filament\Forms\Components\Select;
use Filament\Tables\Actions\AssociateAction;
AssociateAction::make()
->recordSelect(
fn (Select $select) => $select->placeholder('Select a post'),
)
查看关联记录
生成关联管理器时,你可以使用 --view
标记,同时将 ViewAction
添加到表格中:
php artisan make:filament-relation-manager CategoryResource posts title --view
此外,如果你已经生成了关联管理器,可以将 ViewAction
添加到 $table->actions()
数组中:
use Filament\Tables;
use Filament\Tables\Table;
public function table(Table $table): Table
{
return $table
->columns([
// ...
])
->actions([
Tables\Actions\ViewAction::make(),
// ...
]);
}
删除关联记录
默认情况下,你不能在关联管理器中和已删除的记录交互。如果你想在关联管理器中添加恢复,强制删除和过滤废弃删除记录这样的功能,请在生成关联管理器时使用 --soft-deletes 标志:
php artisan make:filament-relation-manager CategoryResource posts title --soft-deletes
另外,你可以在现有关联管理器中添加软删除功能:
use Filament\Tables;
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([
Tables\Filters\TrashedFilter::make(),
// ...
])
->actions([
Tables\Actions\DeleteAction::make(),
Tables\Actions\ForceDeleteAction::make(),
Tables\Actions\RestoreAction::make(),
// ...
])
->bulkActions([
BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
Tables\Actions\ForceDeleteBulkAction::make(),
Tables\Actions\RestoreBulkAction::make(),
// ...
]),
]);
}
自定义 DeleteAction
要学会如何自定义 DeleteAction
,包括更改表单数据、修改通知及添加生命周期钩子,请查阅 Action 文档。
访问关联拥有者的记录
关联管理器是 Livewire 组件。当它们首次加载时,拥有者的记录(作为父级模型 —— 即主资源模型 —— 的 Eloquent 记录) 将保存到属性上。你可以这样读取该属性:
$this->getOwnerRecord()
不过,在静态方法,如 form()
或 table()
中,无法访问 $this
。因此,你可以使用回调函数访问 $livewire
实例:
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Select::make('store_id')
->options(function (RelationManager $livewire): array {
return $livewire->getOwnerRecord()->stores()
->pluck('name', 'id')
->toArray();
}),
// ...
]);
}
Filament 中的所有方法都接收回调,你可以借此访问 $livewire->ownerRecord
:
关联管理器分组
你可以将关联管理器分组到一个标签页中。在 RelationGroup
对象中带上标签包裹多个关联管理器可以实现分组:
use Filament\Resources\RelationManagers\RelationGroup;
public static function getRelations(): array
{
return [
// ...
RelationGroup::make('Contacts', [
RelationManagers\IndividualsRelationManager::class,
RelationManagers\OrganizationsRelationManager::class,
]),
// ...
];
}