Skip to content
全部文档

管理关联

选择合适的工具

Filament 提供多种方式管理应用中的关联。应使用哪种功能,取决于你管理的关联类型,以及期望的 UI。

关联管理器 - 资源表单下方的交互式表格

INFO

兼容 HasManyHasManyThroughBelongsToManyMorphManyMorphToMany 关联。

关联管理器 是交互式表格,允许管理员在不离开资源编辑页或查看页的情况下,列出、创建、附加、关联、编辑、分离、解除关联并删除相关记录。

选择器与复选框列表 - 从现有记录中选择或新建

INFO

兼容 BelongsToMorphToBelongsToMany 关联。

使用 选择器,用户可从现有记录列表中选择。也可 添加按钮,在模态中创建新记录,而无需离开页面。

BelongsToMany 关联上使用选择器时,可选择多个选项,而不只是一个。提交表单时记录会自动加入中间表。若愿意,可将多选下拉替换为简单的 复选框列表。两种组件工作方式相同。

INFO

兼容 HasManyMorphMany 关联。

重复器 是标准表单组件,可无限渲染可重复的字段组。它们可挂接到关联,从而自动从相关表读取、创建、更新与删除记录。它们位于主表单 schema 内,可用于资源页面,也可嵌套在操作模态中。

从 UX 角度看,仅当相关模型字段较少时才适合此方案,否则表单会变得很长。

布局表单组件 - 将表单字段保存到单个关联

INFO

兼容 BelongsToHasOneMorphOne 关联。

所有布局表单组件(GridSectionFieldset 等)都有 relationship() 方法。使用后,该布局内的所有字段会保存到相关模型,而不是所有者模型:

php
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'),
    ])

在此例中,titledescriptionimage 会自动从 metadata 关联加载,并在提交表单时再次保存。若 metadata 记录不存在,会自动创建。

此功能在 Forms 文档 中有更深入说明。请访问该页了解用法。

创建关联管理器

要创建关联管理器,可使用 make:filament-relation-manager 命令:

bash
php artisan make:filament-relation-manager CategoryResource posts title
  • CategoryResource 是所有者(父)模型的资源类名。
  • posts 是要管理的关联名称。
  • title 是用于标识 posts 的属性名。

这会创建 CategoryResource/RelationManagers/PostsRelationManager.php 文件。该类中可为关联管理器定义 表单表格

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() 方法中注册新的关联管理器:

php
public static function getRelations(): array
{
    return [
        RelationManagers\PostsRelationManager::class,
    ];
}

为关联管理器定义表格与表单后,访问资源的 编辑查看 页即可看到效果。

关联管理器关联管理器

自定义关联管理器的 URL 参数

若向 getRelations() 返回的数组传入键,切换多个关联管理器时会在 URL 中使用该键。例如,可传入 posts,使 URL 使用 ?relation=posts 而非数字数组索引:

php
public static function getRelations(): array
{
    return [
        'posts' => RelationManagers\PostsRelationManager::class,
    ];
}

只读模式

关联管理器通常显示在资源的编辑页或查看页。在查看页上,Filament 会自动隐藏所有会修改关联的操作,例如创建、编辑与删除。我们称之为「只读模式」,默认启用以保持查看页的只读行为。不过,可通过覆盖关联管理器类上的 isReadOnly(),使其始终返回 false 来禁用此行为:

php
public function isReadOnly(): bool
{
    return false;
}

或者,若很不喜欢此功能,可在面板 配置 中一次性为所有关联管理器禁用:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->readOnlyRelationManagersOnResourceViewPagesByDefault(false);
}

非传统的反向关联名

对于不遵循 Laravel 命名规范的反向关联,可在表格上使用 inverseRelationship() 方法:

php
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 标志:

bash
php artisan make:filament-relation-manager CategoryResource posts title --soft-deletes

可在 此处 了解更多软删除相关内容。

相关记录会列在表格中。整个关联管理器围绕此表格构建,其中包含 创建编辑附加 / 分离关联 / 解除关联 以及删除记录的操作。

可使用 表格构建器 的任意功能来自定义关联管理器。

列出中间表属性

对于 BelongsToManyMorphToMany 关联,还可添加中间表属性。例如,若有用于 UserResourceTeamsRelationManager,并希望将 role 中间表属性加入表格,可使用:

php
use Filament\Tables;

public function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name'),
            Tables\Columns\TextColumn::make('role'),
        ]);
}

请确保所有中间表属性都已列在关联 以及 反向关联的 withPivot() 方法中。

创建时带中间表属性

对于 BelongsToManyMorphToMany 关联,还可添加中间表属性。例如,若有用于 UserResourceTeamsRelationManager,并希望将 role 中间表属性加入创建表单,可使用:

php
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,包括变更表单数据、更改通知以及添加生命周期钩子,请参阅 操作文档

编辑时带中间表属性

对于 BelongsToManyMorphToMany 关联,还可编辑中间表属性。例如,若有用于 UserResourceTeamsRelationManager,并希望将 role 中间表属性加入编辑表单,可使用:

php
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,包括变更表单数据、更改通知以及添加生命周期钩子,请参阅 操作文档

附加与分离记录

Filament 能够为 BelongsToManyMorphToMany 关联附加与分离记录。

生成关联管理器时,可传入 --attach 标志,以同时向表格添加 AttachActionDetachActionDetachBulkAction

bash
php artisan make:filament-relation-manager CategoryResource posts title --attach

或者,若已生成资源,只需将这些操作加入 $table 数组:

php
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(),
            ]),
        ]);
}
关联管理器附加模态关联管理器附加模态

DANGER

AssociateActionAttachActionDetachActionDissociateAction(及其批量变体)仅检查关联管理器的 isReadOnly() 状态——默认不会查阅任何模型策略方法。批量删除、强制删除与恢复操作会使用 deleteAny()forceDeleteAny()restoreAny() 策略方法(整批一次调用)以兼顾性能。若需要在批量操作上做逐条授权,请对其调用 authorizeIndividualRecords('ability'),并接受额外的查询开销。

预加载附加模态的选择选项

默认情况下,搜索要附加的记录时,选项会通过 AJAX 从数据库加载。若希望在表单首次加载时预加载这些选项,可使用 AttachActionpreloadRecordSelect() 方法:

php
use Filament\Actions\AttachAction;

AttachAction::make()
    ->preloadRecordSelect()

附加时带中间表属性

Attach 按钮附加记录时,你可能希望定义自定义表单,为关联添加中间表属性:

php
use Filament\Actions\AttachAction;
use Filament\Forms;

AttachAction::make()
    ->schema(fn (AttachAction $action): array => [
        $action->getRecordSelect(),
        Forms\Components\TextInput::make('role')->required(),
    ])

在此例中,$action->getRecordSelect() 返回用于选择要附加记录的选择字段。role 文本输入随后会保存到中间表的 role 列。

请确保所有中间表属性都已列在关联 以及 反向关联的 withPivot() 方法中。

限定可附加的选项

你可能希望限定 AttachAction 可用的选项:

php
use Filament\Actions\AttachAction;
use Illuminate\Database\Eloquent\Builder;

AttachAction::make()
    ->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))

跨多列搜索可附加选项

默认情况下,AttachAction 的可用选项会在表格的 recordTitleAttribute() 中搜索。若希望跨多列搜索,可使用 recordSelectSearchColumns() 方法:

php
use Filament\Actions\AttachAction;

AttachAction::make()
    ->recordSelectSearchColumns(['title', 'description'])

附加多条记录

AttachAction 组件上的 multiple() 方法允许选择多个值:

php
use Filament\Actions\AttachAction;

AttachAction::make()
    ->multiple()

自定义附加模态中的选择字段

可通过向 recordSelect() 方法传入函数,自定义附加过程中使用的选择字段对象:

php
use Filament\Actions\AttachAction;
use Filament\Forms\Components\Select;

AttachAction::make()
    ->recordSelect(
        fn (Select $select) => $select->placeholder('Select a post'),
    )

用模态表格选择要附加的记录

可使用 tableSelect() 方法,在附加模态中用完整的 Filament 表格选择记录,而不是简单的选择下拉:

php
use App\Filament\Resources\Products\Tables\ProductsTable;
use Filament\Actions\AttachAction;

AttachAction::make()
    ->tableSelect(ProductsTable::class)

在此例中,ProductsTable 是标准的 Filament 表格类,其 configure() 方法定义表格的列:

php
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;

public static function configure(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('name'),
            TextColumn::make('sku'),
            // ...
        ])
        ->filters([
            // ...
        ]);
}

DANGER

筛选表格查询(通过 modifyQueryUsing()、筛选器或表格参数)是展示性的——它只影响显示哪些记录供选择。它不是安全边界:篡改提交的模态状态的用户,仍可能附加未出现在可见表格中的记录。

要限制实际可附加的记录,请用 recordSelectOptionsQuery() 方法 限定选项。Filament 会按该查询解析提交的记录,因此查询之外的记录会被拒绝:

php
use App\Filament\Resources\Products\Tables\ProductsTable;
use Filament\Actions\AttachAction;
use Illuminate\Database\Eloquent\Builder;

AttachAction::make()
    ->tableSelect(ProductsTable::class)
    ->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))

处理重复

默认情况下,不允许多次附加同一条记录。因为要使此功能生效,还必须在中间表上设置主键 id 列。

请确保 id 属性已列在关联 以及 反向关联的 withPivot() 方法中。

最后,向表格添加 allowDuplicates() 方法:

php
public function table(Table $table): Table
{
    return $table
        ->allowDuplicates();
}

提升分离批量操作的性能

默认情况下,DetachBulkAction 会将所有 Eloquent 记录加载到内存,再逐条循环分离。

若要分离大量记录,可用 chunkSelectedRecords() 方法每次获取较少数量的记录,以降低应用内存占用:

php
use Filament\Actions\DetachBulkAction;

DetachBulkAction::make()
    ->chunkSelectedRecords(250)

Filament 在分离前将 Eloquent 记录加载到内存,出于两个原因:

  • 以便在分离前用模型策略对集合中的单条记录授权(例如使用 `authorizeIndividualRecords('delete')`)。
  • 以确保分离记录时会运行模型事件,例如模型观察者中的 `deleting` 与 `deleted` 事件。

若不需要逐条记录策略授权与模型事件,可使用 fetchSelectedRecords(false),分离前不会将记录加载到内存,而是用单次查询分离:

php
use Filament\Actions\DetachBulkAction;

DetachBulkAction::make()
    ->fetchSelectedRecords(false)

关联与解除关联记录

Filament 能够为 HasManyMorphMany 关联进行关联与解除关联。

生成关联管理器时,可传入 --associate 标志,以同时向表格添加 AssociateActionDissociateActionDissociateBulkAction

bash
php artisan make:filament-relation-manager CategoryResource posts title --associate

或者,若已生成资源,只需将这些操作加入 $table 数组:

php
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(),
            ]),
        ]);
}

预加载关联模态的选择选项

默认情况下,搜索要关联的记录时,选项会通过 AJAX 从数据库加载。若希望在表单首次加载时预加载这些选项,可使用 AssociateActionpreloadRecordSelect() 方法:

php
use Filament\Actions\AssociateAction;

AssociateAction::make()
    ->preloadRecordSelect()

限定可关联的选项

你可能希望限定 AssociateAction 可用的选项:

php
use Filament\Actions\AssociateAction;
use Illuminate\Database\Eloquent\Builder;

AssociateAction::make()
    ->recordSelectOptionsQuery(fn (Builder $query) => $query->whereBelongsTo(auth()->user()))

跨多列搜索可关联选项

默认情况下,AssociateAction 的可用选项会在表格的 recordTitleAttribute() 中搜索。若希望跨多列搜索,可使用 recordSelectSearchColumns() 方法:

php
use Filament\Actions\AssociateAction;

AssociateAction::make()
    ->recordSelectSearchColumns(['title', 'description'])

关联多条记录

AssociateAction 组件上的 multiple() 方法允许选择多个值:

php
use Filament\Actions\AssociateAction;

AssociateAction::make()
    ->multiple()

自定义关联模态中的选择字段

可通过向 recordSelect() 方法传入函数,自定义关联过程中使用的选择字段对象:

php
use Filament\Actions\AssociateAction;
use Filament\Forms\Components\Select;

AssociateAction::make()
    ->recordSelect(
        fn (Select $select) => $select->placeholder('Select a post'),
    )

提升解除关联批量操作的性能

默认情况下,DissociateBulkAction 会将所有 Eloquent 记录加载到内存,再逐条循环解除关联。

若要解除关联大量记录,可用 chunkSelectedRecords() 方法每次获取较少数量的记录,以降低应用内存占用:

php
use Filament\Actions\DissociateBulkAction;

DissociateBulkAction::make()
    ->chunkSelectedRecords(250)

Filament 在解除关联前将 Eloquent 记录加载到内存,出于两个原因:

  • 以便在解除关联前用模型策略对集合中的单条记录授权(例如使用 `authorizeIndividualRecords('update')`)。
  • 以确保解除关联时会运行模型事件,例如模型观察者中的 `updating` 与 `updated` 事件。

若不需要逐条记录策略授权与模型事件,可使用 fetchSelectedRecords(false),解除关联前不会将记录加载到内存,而是用单次查询解除关联:

php
use Filament\Actions\DissociateBulkAction;

DissociateBulkAction::make()
    ->fetchSelectedRecords(false)

生成关联管理器时,可传入 --view 标志,以同时向表格添加 ViewAction

bash
php artisan make:filament-relation-manager CategoryResource posts title --view

或者,若已生成关联管理器,只需将 ViewAction 加入 $table->recordActions() 数组:

php
use Filament\Actions\ViewAction;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->columns([
            // ...
        ])
        ->recordActions([
            ViewAction::make(),
            // ...
        ]);
}

默认情况下,你无法在关联管理器中操作已删除的记录。若希望在关联管理器中恢复、强制删除并筛选已删除记录,生成关联管理器时请使用 --soft-deletes 标志:

bash
php artisan make:filament-relation-manager CategoryResource posts title --soft-deletes

或者,可为现有关联管理器添加软删除功能:

php
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,包括更改通知以及添加生命周期钩子,请参阅 操作文档

可将 ImportAction 添加到关联管理器的页眉以导入记录。此时,你可能希望告知 importer 这些新记录属于哪个所有者。可用 导入选项 传入所有者记录的 ID:

php
ImportAction::make()
    ->importer(ProductImporter::class)
    ->options(['categoryId' => $this->getOwnerRecord()->getKey()])

现在,在 importer 类中,可将所有者与导入记录建立一对多关联:

php
public function resolveRecord(): ?Product
{
    $product = Product::firstOrNew([
        'sku' => $this->data['sku'],
    ]);
    
    $product->category()->associate($this->options['categoryId']);
    
    return $product;
}

或者,可用 importer 的 afterSave() 钩子,在多对多关联中附加记录:

php
protected function afterSave(): void
{
    $this->record->categories()->syncWithoutDetaching([$this->options['categoryId']]);
}

访问关联的所有者记录

关联管理器是 Livewire 组件。首次加载时,所有者记录(作为父级的 Eloquent 记录——主资源模型)会保存到一个属性中。可读该属性:

php
$this->getOwnerRecord()

不过,若在 form()table()static 方法中,无法访问 $this。因此,可 使用回调 访问 $livewire 实例:

php
use Filament\Forms;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Schemas\Schema;

public function form(Schema $schema): Schema
{
    return $schema
        ->components([
            Forms\Components\Select::make('store_id')
                ->options(function (RelationManager $livewire): array {
                    return $livewire->getOwnerRecord()->stores()
                        ->pluck('name', 'id')
                        ->toArray();
                }),
            // ...
        ]);
}

Filament 中的所有方法都接受回调,你可在其中访问 $livewire->ownerRecord

分组关联管理器

你可选择将关联管理器分到同一标签页。为此,可用带标签的 RelationGroup 对象包装多个管理器:

php
use Filament\Resources\RelationManagers\RelationGroup;

public static function getRelations(): array
{
    return [
        RelationGroup::make('Interactions', [
            RelationManagers\CommentsRelationManager::class,
            RelationManagers\TagsRelationManager::class,
        ]),
        RelationGroup::make('Links', [
            RelationManagers\LinksRelationManager::class,
        ]),
    ];
}
带分组标签的关联管理器带分组标签的关联管理器

有条件地显示关联管理器

默认情况下,若相关模型策略的 viewAny() 方法返回 true,关联管理器可见。

可用 canViewForRecord() 方法判断关联管理器是否应对特定所有者记录与页面可见:

php
use Illuminate\Database\Eloquent\Model;

public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
{
    return $ownerRecord->status === Status::Draft;
}

将关联管理器标签页与表单合并

在编辑页或查看页类上,覆盖 hasCombinedRelationManagerTabsWithContent() 方法:

php
public function hasCombinedRelationManagerTabsWithContent(): bool
{
    return true;
}
合并关联管理器标签页的资源编辑页面合并关联管理器标签页的资源编辑页面

自定义内容标签页

在编辑页或查看页类上,覆盖 getContentTabComponent() 方法,并使用任意 Tab 自定义方法:

php
use Filament\Schemas\Components\Tabs\Tab;

public function getContentTabComponent(): Tab
{
    return Tab::make('Settings')
        ->icon('heroicon-m-cog');
}

设置表单标签页位置

默认情况下,表单标签页渲染在关联标签页之前。要将其渲染在之后,可在编辑页或查看页类上覆盖 getContentTabPosition() 方法:

php
use Filament\Resources\Pages\Enums\ContentTabPosition;

public function getContentTabPosition(): ?ContentTabPosition
{
    return ContentTabPosition::After;
}

自定义关联管理器标签页

要自定义关联管理器的标签页,请覆盖 getTabComponent() 方法,并使用任意 Tab 自定义方法:

php
use Filament\Schemas\Components\Tabs\Tab;
use Illuminate\Database\Eloquent\Model;

public static function getTabComponent(Model $ownerRecord, string $pageClass): Tab
{
    return Tab::make('Blog posts')
        ->badge($ownerRecord->posts()->count())
        ->badgeColor('info')
        ->badgeTooltip('The number of posts in this category')
        ->icon('heroicon-m-document-text');
}

TIP

除静态值外,badgeColor()badgeTooltip() 方法也接受函数以动态计算。可将各种工具作为参数注入这些函数。

若使用 关联分组,可使用 tab() 方法:

php
use Filament\Resources\RelationManagers\RelationGroup;
use Filament\Schemas\Components\Tabs\Tab;
use Illuminate\Database\Eloquent\Model;

RelationGroup::make('Contacts', [
    // ...
])
    ->tab(fn (Model $ownerRecord): Tab => Tab::make('Blog posts')
        ->badge($ownerRecord->posts()->count())
        ->badgeColor('info')
        ->badgeTooltip('The number of posts in this category')
        ->icon('heroicon-m-document-text'));

TIP

除静态值外,badgeColor()badgeTooltip() 方法也接受函数以动态计算。可将各种工具作为参数注入这些函数。

延迟加载关联管理器标签页徽章

getBadge() 会运行昂贵查询,可将 $isBadgeDeferred 属性设为 true,以延迟徽章,使其在页面渲染后异步加载:

php
use Illuminate\Database\Eloquent\Model;

protected static bool $isBadgeDeferred = true;

public static function getBadge(Model $ownerRecord, string $pageClass): ?string
{
    $count = $ownerRecord->tickets()->count();

    return $count > 0 ? (string) $count : null;
}

或者,可覆盖 isBadgeDeferred() 方法以定义动态行为:

php
use Illuminate\Database\Eloquent\Model;

public static function isBadgeDeferred(Model $ownerRecord, string $pageClass): bool
{
    return FeatureFlag::active();
}

若使用 关联分组,请使用链式 deferBadge() 方法:

php
use Filament\Resources\RelationManagers\RelationGroup;

RelationGroup::make('Contacts', [
    // ...
])
    ->badge(fn (Model $ownerRecord): string => (string) $ownerRecord->contacts()->count())
    ->deferBadge();

与关联管理器共享资源的表单与表格

你可能希望资源的表单与表格与关联管理器完全相同,从而复用已写的代码。这很简单:从关联管理器调用资源的 form()table() 方法即可:

php
use App\Filament\Resources\Blog\Posts\PostResource;
use Filament\Schemas\Schema;
use Filament\Tables\Table;

public function form(Schema $schema): Schema
{
    return PostResource::form($schema);
}

public function table(Table $table): Table
{
    return PostResource::table($table);
}

在关联管理器上隐藏共享的表单组件

若将资源的表单组件与关联管理器共享,可能希望在关联管理器上隐藏它。这在希望隐藏所有者记录的 Select 字段时特别有用,因为 Filament 本就会替你处理。为此,可使用 hiddenOn() 方法,并传入关联管理器名称:

php
use App\Filament\Resources\Blog\Posts\PostResource\RelationManagers\CommentsRelationManager;
use Filament\Forms\Components\Select;

Select::make('post_id')
    ->relationship('post', 'title')
    ->hiddenOn(CommentsRelationManager::class)

在关联管理器上隐藏共享的表格列

若将资源的表格列与关联管理器共享,可能希望在关联管理器上隐藏它。这在希望隐藏所有者记录列时特别有用,因为所有者记录已列在关联管理器上方,再显示不合适。为此,可使用 hiddenOn() 方法,并传入关联管理器名称:

php
use App\Filament\Resources\Blog\Posts\PostResource\RelationManagers\CommentsRelationManager;
use Filament\Tables\Columns\TextColumn;

TextColumn::make('post.title')
    ->hiddenOn(CommentsRelationManager::class)

在关联管理器上隐藏共享的表格筛选器

若将资源的表格筛选器与关联管理器共享,可能希望在关联管理器上隐藏它。这在希望隐藏所有者记录筛选器时特别有用,因为表格已按所有者记录筛选。为此,可使用 hiddenOn() 方法,并传入关联管理器名称:

php
use App\Filament\Resources\Blog\Posts\PostResource\RelationManagers\CommentsRelationManager;
use Filament\Tables\Filters\SelectFilter;

SelectFilter::make('post')
    ->relationship('post', 'title')
    ->hiddenOn(CommentsRelationManager::class)

在关联管理器上覆盖共享配置

在资源内做的任何配置都可在关联管理器上覆盖。例如,若希望在关联管理器继承的表格上禁用分页,但不影响资源本身:

php
use App\Filament\Resources\Blog\Posts\PostResource;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return PostResource::table($table)
        ->paginated(false);
}

若希望在关联管理器上添加页眉操作以 创建附加关联 记录,在关联管理器上提供额外配置也很有用:

php
use App\Filament\Resources\Blog\Posts\PostResource;
use Filament\Actions\AttachAction;
use Filament\Actions\CreateAction;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return PostResource::table($table)
        ->headerActions([
            CreateAction::make(),
            AttachAction::make(),
        ]);
}

自定义关联管理器 Eloquent 查询

可应用影响整个关联管理器的自定义查询约束或 模型作用域。为此,可向表格的 modifyQueryUsing() 方法传入函数,并在其中自定义查询:

php
use Filament\Tables;
use Illuminate\Database\Eloquent\Builder;

public function table(Table $table): Table
{
    return $table
        ->modifyQueryUsing(fn (Builder $query) => $query->where('is_active', true))
        ->columns([
            // ...
        ]);
}

DANGER

modifyQueryUsing() 会限定已属于该关联的记录查询——表格列表,以及作用于其行的操作,如 DetachActionDissociateAction 与批量操作。它不会应用于 AttachActionAssociateAction 可用的记录,因为这些记录按定义在关联之外。

要限制可附加或关联的记录,请在操作上用 recordSelectOptionsQuery() 限定选项。Filament 会按该查询解析提交的记录,因此即使有人篡改提交的模态状态,查询之外的记录也会被拒绝:

php
use Filament\Actions\AttachAction;
use Illuminate\Database\Eloquent\Builder;

AttachAction::make()
    ->recordSelectOptionsQuery(fn (Builder $query) => $query->where('is_active', true))

了解更多关于限定 附加关联 选项的内容。

自定义关联管理器标题

要设置关联管理器的标题,可在关联管理器类上使用 $title 属性:

php
protected static ?string $title = 'Posts';

要动态设置关联管理器标题,可在关联管理器类上覆盖 getTitle() 方法:

php
use Illuminate\Database\Eloquent\Model;

public static function getTitle(Model $ownerRecord, string $pageClass): string
{
    return __('relation-managers.posts.title');
}

标题会反映在 表格标题 中;若有多个关联管理器,也会反映在标签页上。若希望单独自定义表格标题,仍可使用 $table->heading() 方法:

php
use Filament\Tables;

public function table(Table $table): Table
{
    return $table
        ->heading('Posts')
        ->columns([
            // ...
        ]);
}

自定义关联管理器记录标题

关联管理器使用「记录标题属性」的概念,来决定用相关模型的哪个属性标识记录。创建关联管理器时,该属性作为 make:filament-relation-manager 命令的第三个参数传入:

bash
php artisan make:filament-relation-manager CategoryResource posts title

在此例中,Post 模型的 title 属性会用于在关联管理器中标识文章。

这主要由操作类使用。例如,当你 附加关联 记录时,标题会列在选择字段中。当你 编辑查看删除 记录时,标题会用在模态页眉中。

有时你可能希望拼接多个属性形成标题。可将 recordTitleAttribute() 配置方法替换为 recordTitle(),并传入将模型转换为标题的函数:

php
use App\Models\Post;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->recordTitle(fn (Post $record): string => "{$record->title} ({$record->id})")
        ->columns([
            // ...
        ]);
}

若使用 recordTitle(),并且有 关联操作附加操作,还应为这些操作指定搜索列:

php
use Filament\Actions\AssociateAction;
use Filament\Actions\AttachAction;

AssociateAction::make()
    ->recordSelectSearchColumns(['title', 'id']);

AttachAction::make()
    ->recordSelectSearchColumns(['title', 'id'])

关联页面

若希望将管理关联的功能与编辑或查看所有者记录分开,可使用 ManageRelatedRecords 页面作为关联管理器的替代方案。

若使用 资源子导航,此功能很理想,因为你可以方便地在查看页或编辑页与关联页面之间切换。

要创建关联页面,应使用 make:filament-page 命令:

bash
php artisan make:filament-page ManageCustomerAddresses --resource=CustomerResource --type=ManageRelatedRecords

运行此命令时,会被问一系列问题以自定义页面,例如关联名称及其标题属性。

你必须在资源的 getPages() 方法中注册该新页面:

php
public static function getPages(): array
{
    return [
        'index' => Pages\ListCustomers::route('/'),
        'create' => Pages\CreateCustomer::route('/create'),
        'view' => Pages\ViewCustomer::route('/{record}'),
        'edit' => Pages\EditCustomer::route('/{record}/edit'),
        'addresses' => Pages\ManageCustomerAddresses::route('/{record}/addresses'),
    ];
}

WARNING

使用关联页面时,无需用 make:filament-relation-manager 生成关联管理器,也无需在资源的 getRelations() 方法中注册它。

现在,可以与关联管理器完全相同的方式自定义该页面,使用同样的 table()form()

将关联页面加入资源子导航

若使用 资源子导航,可在资源的 getRecordSubNavigation() 中照常注册该页面:

php
use App\Filament\Resources\Customers\Pages;
use Filament\Resources\Pages\Page;

public static function getRecordSubNavigation(Page $page): array
{
    return $page->generateNavigationItems([
        // ...
        Pages\ManageCustomerAddresses::class,
    ]);
}

向关联管理器传递属性

在资源中注册关联管理器时,可用 make() 方法向其传递 Livewire 属性 数组:

php
use App\Filament\Resources\Blog\Posts\PostResource\RelationManagers\CommentsRelationManager;

public static function getRelations(): array
{
    return [
        CommentsRelationManager::make([
            'status' => 'approved',
        ]),
    ];
}

该属性数组会映射到关联管理器类上的 公开 Livewire 属性

php
use Filament\Resources\RelationManagers\RelationManager;

class CommentsRelationManager extends RelationManager
{
    public string $status;

    // ...
}

现在,可在关联管理器类中用 $this->status 访问 status

禁用懒加载

默认情况下,关联管理器是懒加载的。这意味着它们仅在页面上可见时才会加载。

要禁用此行为,可在关联管理器类上覆盖 $isLazy 属性:

php
protected static bool $isLazy = false;