Languages

Version

Theme

资源

管理关联

选择合适的工具

Filament 提供了多种在应用中管理管理的方法。选择哪个方式取决于你管理的关联类型和你希望的 UI 界面。

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

NOTE

该方式兼容 HasManyHasManyThroughBelongsToManyMorphManyMorphToMany 关联。

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

Select & checkbox list - 从现有记录中选择或者创建新记录

NOTE

该方式兼容 BelongsToMorphToBelongsToMany 关联。

使用 Select,用户可以从已有记录清单中进行选择。你页可以添加一个允许用户在模态框中创建新纪录的按钮,而无需离开页面。

当在 Select 中使用 BelongsToMany 关联时,你可以选择多个选项。记录会在你提交表单时,自动添加到中间表中。你也可以使用 Checkbox List替换多选下拉框。这两个组件的工作原理相同。

Repeater - 在表单中 CRUD 多个关联纪律

NOTE

该方式兼容 HasManyMorphMany 关联。

Repeater是标准的表单组件,可以无限渲染一组可重复的字段。它们可以钩入到关联中,因此记录会自动从相关表中读取、创建、更新和删除。它们位于主表单 Schema 中,可以在资源页面中使用,也可以在嵌套到操作模态框中。

就用户体验角度而言,该方案只适合于关联模型中只有少数几个字段。否则,表单可能会变得非常长。

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

NOTE

该方式兼容 BelongsToHasOneMorphOne 关联。

所有布局表单组件(GridSectionFieldset等)都由一个 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'),
    ])

本例中,titledescriptionimage 自动从 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

你可以在此处查阅更多软删除的信息。

列出关联记录

关联记录会在表格中列出。整个关联管理器都是基于此表格,包含创建编辑附加/分离关联/取消关联和删除记录的操作。

你可以使用表格构造器的所有特性来自定义关联管理器。

使用中间表属性罗列

对于 BelongsToManyMorphToMany 关联,你也可以添加中间表属性。比如你的 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() 方法中列出。

创建关联记录

使用中间表属性创建关联

对于 BelongsToManyMorphToMany 关联,你也可以添加中间表属性。比如你的 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 文档

编辑关联记录

使用中间表属性编辑

对于 BelongsToManyMorphToMany 关联,你也可以添加中间表属性。比如你的 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 可以为 BelongsToManyMorphToMany 关联附加和分离记录。

生成关联管理器时,你可以传入 --attach 标志,以同时添加 AttachActionDetachActionDetachBulkAction 操作到表格中:

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

Preloading the attachment modal select options

By default, as you search for a record to attach, options will load from the database via AJAX. If you wish to preload these options when the form is first loaded instead, you can use the preloadRecordSelect() method of AttachAction:

use Filament\Actions\AttachAction;

AttachAction::make()
    ->preloadRecordSelect()

Attaching with pivot attributes

When you attach record with the Attach button, you may wish to define a custom form to add pivot attributes to the relationship:

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

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

In this example, $action->getRecordSelect() returns the select field to pick the record to attach. The role text input is then saved to the pivot table’s role column.

请确保所有的中间表属性都在关联及反转关联的 withPivot() 方法中列出

Scoping the options to attach

You may want to scope the options available to AttachAction:

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

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

Searching the options to attach across multiple columns

By default, the options available to AttachAction will be searched in the recordTitleAttribute() of the table. If you wish to search across multiple columns, you can use the recordSelectSearchColumns() method:

use Filament\Actions\AttachAction;

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

Attaching multiple records

The multiple() method on the AttachAction component allows you to select multiple values:

use Filament\Actions\AttachAction;

AttachAction::make()
    ->multiple()

Customizing the select field in the attached modal

You may customize the select field object that is used during attachment by passing a function to the recordSelect() method:

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

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

Handling duplicates

By default, you will not be allowed to attach a record more than once. This is because you must also set up a primary id column on the pivot table for this feature to work.

Please ensure that the id attribute is listed in the withPivot() method of the relationship and inverse relationship.

Finally, add the allowDuplicates() method to the table:

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

Improving the performance of detach bulk actions

By default, the DetachBulkAction will load all Eloquent records into memory, before looping over them and detaching them one by one.

If you are detaching a large number of records, you may want to use the chunkSelectedRecords() method to fetch a smaller number of records at a time. This will reduce the memory usage of your application:

use Filament\Actions\DetachBulkAction;

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

Filament loads Eloquent records into memory before detaching them for two reasons:

  • To allow individual records in the collection to be authorized with a model policy before detaching (using authorizeIndividualRecords('delete'), for example).
  • To ensure that model events are run when detaching records, such as the deleting and deleted events in a model observer.

If you do not require individual record policy authorization and model events, you can use the fetchSelectedRecords(false) method, which will not fetch the records into memory before detaching them, and instead will detach them in a single query:

use Filament\Actions\DetachBulkAction;

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

Associating and dissociating records

Filament is able to associate and dissociate records for HasMany and MorphMany relationships.

When generating your relation manager, you may pass the --associate flag to also add AssociateAction, DissociateAction and DissociateBulkAction to the table:

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

Alternatively, if you’ve already generated your resource, you can just add the actions to the $table arrays:

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

Preloading the associate modal select options

By default, as you search for a record to associate, options will load from the database via AJAX. If you wish to preload these options when the form is first loaded instead, you can use the preloadRecordSelect() method of AssociateAction:

use Filament\Actions\AssociateAction;

AssociateAction::make()
    ->preloadRecordSelect()

Scoping the options to associate

You may want to scope the options available to AssociateAction:

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

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

Searching the options to associate across multiple columns

By default, the options available to AssociateAction will be searched in the recordTitleAttribute() of the table. If you wish to search across multiple columns, you can use the recordSelectSearchColumns() method:

use Filament\Actions\AssociateAction;

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

Associating multiple records

The multiple() method on the AssociateAction component allows you to select multiple values:

use Filament\Actions\AssociateAction;

AssociateAction::make()
    ->multiple()

Customizing the select field in the associate modal

You may customize the select field object that is used during association by passing a function to the recordSelect() method:

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

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

Improving the performance of dissociate bulk actions

By default, the DissociateBulkAction will load all Eloquent records into memory, before looping over them and dissociating them one by one.

If you are dissociating a large number of records, you may want to use the chunkSelectedRecords() method to fetch a smaller number of records at a time. This will reduce the memory usage of your application:

use Filament\Actions\DissociateBulkAction;

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

Filament loads Eloquent records into memory before dissociating them for two reasons:

  • To allow individual records in the collection to be authorized with a model policy before dissociation (using authorizeIndividualRecords('update'), for example).
  • To ensure that model events are run when dissociating records, such as the updating and updated events in a model observer.

If you do not require individual record policy authorization and model events, you can use the fetchSelectedRecords(false) method, which will not fetch the records into memory before dissociating them, and instead will dissociate them in a single query:

use Filament\Actions\DissociateBulkAction;

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

When generating your relation manager, you may pass the --view flag to also add a ViewAction to the table:

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

Alternatively, if you’ve already generated your relation manager, you can just add the ViewAction to the $table->recordActions() array:

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

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

By default, you will not be able to interact with deleted records in the relation manager. If you’d like to add functionality to restore, force-delete and filter trashed records in your relation manager, use the --soft-deletes flag when generating the relation manager:

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

Alternatively, you may add soft-deleting functionality to an existing relation manager:

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

Customizing the DeleteAction

To learn how to customize the DeleteAction, including changing the notification and adding lifecycle hooks, please see the Actions documentation.

The ImportAction can be added to the header of a relation manager to import records. In this case, you probably want to tell the importer which owner these new records belong to. You can use import options to pass through the ID of the owner record:

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

Now, in the importer class, you can associate the owner in a one-to-many relationship with the imported record:

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

Alternatively, you can attach the record in a many-to-many relationship using the afterSave() hook of the importer:

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

Accessing the relationship’s owner record

Relation managers are Livewire components. When they are first loaded, the owner record (the Eloquent record which serves as a parent - the main resource model) is saved into a property. You can read this property using:

$this->getOwnerRecord()

However, if you’re inside a static method like form() or table(), $this isn’t accessible. So, you may use a callback to access the $livewire instance:

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

All methods in Filament accept a callback which you can access $livewire->ownerRecord in.

Grouping relation managers

You may choose to group relation managers together into one tab. To do this, you may wrap multiple managers in a RelationGroup object, with a label:

use Filament\Resources\RelationManagers\RelationGroup;

public static function getRelations(): array
{
    return [
        // ...
        RelationGroup::make('Contacts', [
            RelationManagers\IndividualsRelationManager::class,
            RelationManagers\OrganizationsRelationManager::class,
        ]),
        // ...
    ];
}

Conditionally showing relation managers

By default, relation managers will be visible if the viewAny() method for the related model policy returns true.

You may use the canViewForRecord() method to determine if the relation manager should be visible for a specific owner record and page:

use Illuminate\Database\Eloquent\Model;

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

Combining the relation manager tabs with the form

On the Edit or View page class, override the hasCombinedRelationManagerTabsWithContent() method:

public function hasCombinedRelationManagerTabsWithContent(): bool
{
    return true;
}

Customizing the content tab

On the Edit or View page class, override the getContentTabComponent() method, and use any Tab customization methods:

use Filament\Schemas\Components\Tabs\Tab;

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

Setting the position of the form tab

By default, the form tab is rendered before the relation tabs. To render it after, you can override the getContentTabPosition() method on the Edit or View page class:

use Filament\Resources\Pages\Enums\ContentTabPosition;

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

Customizing relation manager tabs

To customize the tab for a relation manager, override the getTabComponent() method, and use any Tab customization methods:

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');
}

If you are using a relation group, you can use the tab() method:

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'));

Sharing a resource’s form and table with a relation manager

You may decide that you want a resource’s form and table to be identical to a relation manager’s, and subsequently want to reuse the code you previously wrote. This is easy, by calling the form() and table() methods of the resource from the relation manager:

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);
}

Hiding a shared form component on the relation manager

If you’re sharing a form component from the resource with the relation manager, you may want to hide it on the relation manager. This is especially useful if you want to hide a Select field for the owner record in the relation manager, since Filament will handle this for you anyway. To do this, you may use the hiddenOn() method, passing the name of the relation manager:

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

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

Hiding a shared table column on the relation manager

If you’re sharing a table column from the resource with the relation manager, you may want to hide it on the relation manager. This is especially useful if you want to hide a column for the owner record in the relation manager, since this is not appropriate when the owner record is already listed above the relation manager. To do this, you may use the hiddenOn() method, passing the name of the relation manager:

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

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

Hiding a shared table filter on the relation manager

If you’re sharing a table filter from the resource with the relation manager, you may want to hide it on the relation manager. This is especially useful if you want to hide a filter for the owner record in the relation manager, since this is not appropriate when the table is already filtered by the owner record. To do this, you may use the hiddenOn() method, passing the name of the relation manager:

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

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

Overriding shared configuration on the relation manager

Any configuration that you make inside the resource can be overwritten on the relation manager. For example, if you wanted to disable pagination on the relation manager’s inherited table but not the resource itself:

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

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

It is probably also useful to provide extra configuration on the relation manager if you wanted to add a header action to create, attach, or associate records in the relation manager:

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

Customizing the relation manager Eloquent query

You can apply your own query constraints or model scopes that affect the entire relation manager. To do this, you can pass a function to the modifyQueryUsing() method of the table, inside which you can customize the query:

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([
            // ...
        ]);
}

Customizing the relation manager title

To set the title of the relation manager, you can use the $title property on the relation manager class:

protected static ?string $title = 'Posts';

To set the title of the relation manager dynamically, you can override the getTitle() method on the relation manager class:

use Illuminate\Database\Eloquent\Model;

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

The title will be reflected in the heading of the table, as well as the relation manager tab if there is more than one. If you want to customize the table heading independently, you can still use the $table->heading() method:

use Filament\Tables;

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

Customizing the relation manager record title

The relation manager uses the concept of a “record title attribute” to determine which attribute of the related model should be used to identify it. When creating a relation manager, this attribute is passed as the third argument to the make:filament-relation-manager command:

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

In this example, the title attribute of the Post model will be used to identify a post in the relation manager.

This is mainly used by the action classes. For instance, when you attach or associate a record, the titles will be listed in the select field. When you edit, view or delete a record, the title will be used in the header of the modal.

In some cases, you may want to concatenate multiple attributes together to form a title. You can do this by replacing the recordTitleAttribute() configuration method with recordTitle(), passing a function that transforms a model into a title:

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([
            // ...
        ]);
}

If you’re using recordTitle(), and you have an associate action or attach action, you will also want to specify search columns for those actions:

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

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

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

Relation pages

Using a ManageRelatedRecords page is an alternative to using a relation manager, if you want to keep the functionality of managing a relationship separate from editing or viewing the owner record.

This feature is ideal if you are using resource sub-navigation, as you are easily able to switch between the View or Edit page and the relation page.

To create a relation page, you should use the make:filament-page command:

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

When you run this command, you will be asked a series of questions to customize the page, for example, the name of the relationship and its title attribute.

You must register this new page in your resource’s getPages() method:

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

NOTE

When using a relation page, you do not need to generate a relation manager with make:filament-relation-manager, and you do not need to register it in the getRelations() method of the resource.

Now, you can customize the page in exactly the same way as a relation manager, with the same table() and form().

Adding relation pages to resource sub-navigation

If you’re using resource sub-navigation, you can register this page as normal in getRecordSubNavigation() of the resource:

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

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

Passing properties to relation managers

When registering a relation manager in a resource, you can use the make() method to pass an array of Livewire properties to it:

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

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

This array of properties gets mapped to public Livewire properties on the relation manager class:

use Filament\Resources\RelationManagers\RelationManager;

class CommentsRelationManager extends RelationManager
{
    public string $status;

    // ...
}

Now, you can access the status in the relation manager class using $this->status.

Disabling lazy loading

By default, relation managers are lazy-loaded. This means that they will only be loaded when they are visible on the page.

To disable this behavior, you may override the $isLazy property on the relation manager class:

protected static bool $isLazy = false;
Edit on GitHub

Still need help? Join our Discord community or open a GitHub discussion

Previous
删除记录