Skip to content
全部文档

操作

简介

Filament 的表格可以使用操作(Actions)。它们是可添加到任意表格行末尾,甚至表格页眉工具栏的按钮。例如,你可能希望在页眉有「创建」新记录的操作,并在每行上有「编辑」和「删除」操作。批量操作可在选中表格记录时执行代码。此外,操作也可添加到任意表格列,使该列中的每个单元格成为操作的触发器。

强烈建议阅读关于自定义操作触发按钮操作模态框的文档,以便了解操作的全部能力。

记录操作

操作按钮可渲染在每行表格的末尾。可将它们放在 $table->recordActions() 方法中:

php
use Filament\Tables\Table;

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

操作可使用静态 make() 方法创建,并传入唯一名称。

然后可以向 action() 传入执行任务的函数,或向 url() 传入创建链接的函数:

php
use App\Models\Post;
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (Post $record): string => route('posts.edit', $record))
    ->openUrlInNewTab()

Action::make('delete')
    ->requiresConfirmation()
    ->action(fn (Post $record) => $record->delete())

操作上的所有方法都接受回调函数,你可以在其中访问被点击的当前表格 $record

带操作的表格带操作的表格

将记录操作放在列之前

默认情况下,表格中的记录操作渲染在每行的最后一个单元格中。你可以使用 position 参数将它们移到列之前:

php
use Filament\Tables\Enums\RecordActionsPosition;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->recordActions([
            // ...
        ], position: RecordActionsPosition::BeforeColumns);
}
列之前带操作的表格列之前带操作的表格

将记录操作放在复选框列之前

默认情况下,表格中的记录操作渲染在每行的最后一个单元格中。你可以使用 position 参数将它们移到复选框列之前:

php
use Filament\Tables\Enums\RecordActionsPosition;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->recordActions([
            // ...
        ], position: RecordActionsPosition::BeforeCells);
}
单元格之前带操作的表格单元格之前带操作的表格

全局记录操作设置

要自定义未分组记录操作的默认配置,可以在服务提供者的 boot() 方法中,通过 Table::configureUsing() 函数 使用 modifyUngroupedRecordActionsUsing()

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

Table::configureUsing(function (Table $table): void {
    $table
        ->modifyUngroupedRecordActionsUsing(fn (Action $action) => $action->iconButton());
});

访问选中的表格行

你可能希望某个操作能访问表格中所有选中的行。通常这通过表格页眉中的批量操作完成。不过,你也可能希望用行操作来做这件事,让选中的行作为该操作的上下文。

例如,你可能希望有一个行操作,将行数据复制到所有选中的记录。即使未定义批量操作,也要强制表格可选,需使用 selectable() 方法。要允许操作访问选中的记录,需使用 accessSelectedRecords() 方法。然后可在操作中使用 $selectedRecords 参数访问选中的记录:

php
use Filament\Actions\Action;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;

public function table(Table $table): Table
{
    return $table
        ->selectable()
        ->recordActions([
            Action::make('copyToSelected')
                ->accessSelectedRecords()
                ->action(function (Model $record, Collection $selectedRecords) {
                    $selectedRecords->each(
                        fn (Model $selectedRecord) => $selectedRecord->update([
                            'is_active' => $record->is_active,
                        ]),
                    );
                }),
        ]);
}

批量操作

表格还支持「批量操作」。用户在表格中选中行时可使用它们。传统上,选中行后会出现「批量操作」按钮;用户点击后会看到可选操作的下拉菜单。可将它们放在 $table->toolbarActions()$table->headerActions() 方法中:

php
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            // ...
        ]);
}

批量操作可使用静态 make() 方法创建,并传入唯一名称。然后应向 action() 传入执行任务的回调:

php
use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('delete')
    ->requiresConfirmation()
    ->action(fn (Collection $records) => $records->each->delete())

该函数允许你访问当前表格中选中的 $records。它是模型的 Eloquent 集合。

带批量操作的表格带批量操作的表格

授权批量操作

使用批量操作时,你可以为每条选中的记录检查策略方法。这对检查用户是否有权对每条记录执行该操作很有用。可以使用 authorizeIndividualRecords() 方法,传入策略方法名,该方法会对每条记录调用。若策略拒绝授权,该记录将不会出现在批量操作的 $records 参数中:

php
use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('delete')
    ->requiresConfirmation()
    ->authorizeIndividualRecords('delete')
    ->action(fn (Collection $records) => $records->each->delete())

批量操作通知

批量操作完成后,你可能希望向用户发送带成功摘要的通知。若对单条记录使用了授权,这尤其有用,因为用户可能不知道实际影响了多少条记录。

要在批量操作完成后发送通知,应设置 successNotificationTitle()failureNotificationTitle()

  • `successNotificationTitle()` 在所有记录都成功处理时用作通知标题。
  • `failureNotificationTitle()` 在部分或全部记录处理失败时用作通知标题。向该方法传入函数时,可注入 `$successCount` 和 `$failureCount` 参数,以便向用户提供这些信息。

例如:

php
use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('delete')
    ->requiresConfirmation()
    ->authorizeIndividualRecords('delete')
    ->action(fn (Collection $records) => $records->each->delete())
    ->successNotificationTitle('Deleted users')
    ->failureNotificationTitle(function (int $successCount, int $totalCount): string {
        if ($successCount) {
            return "{$successCount} of {$totalCount} users deleted";
        }

        return 'Failed to delete any users';
    })

你还可以在策略方法中使用特殊的授权响应对象来提供授权失败原因的自定义消息。该特殊对象名为 DenyResponse,可替代 Response::deny(),允许开发者将函数作为消息传入,该函数可接收有多少条记录被该授权检查拒绝的信息:

php
use App\Models\User;
use Filament\Support\Authorization\DenyResponse;
use Illuminate\Auth\Access\Response;

class UserPolicy
{
    public function delete(User $user, User $model): bool | Response
    {
        if (! $model->is_admin) {
            return true;
        }

        return DenyResponse::make('cannot_delete_admin', message: function (int $failureCount, int $totalCount): string {
            if (($failureCount === 1) && ($totalCount === 1)) {
                return 'You cannot delete an admin user.';
            }

            if ($failureCount === $totalCount) {
                return 'All users selected were admin users.';
            }

            if ($failureCount === 1) {
                return 'One of the selected users was an admin user.';
            }

            return "{$failureCount} of the selected users were admin users.";
        });
    }
}

make() 方法的第一个参数是标识该失败类型的唯一键。若检测到该键的多次失败,会归为一组并只生成一条消息。若策略方法中有多个失败点,每个响应对象可有自己的键,消息会在通知中拼接在一起。

在批量操作处理中报告失败

除了单条记录授权消息外,你还可以在批量操作处理本身中报告失败。若希望在授权通过后,仍为因特定原因处理失败的每条记录提供消息,这会很有用。做法是将 Action 实例注入 action() 函数,并在其上调用 reportBulkProcessingFailure() 方法,传入与 DenyResponse 类似的键和消息函数:

php
use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('delete')
    ->requiresConfirmation()
    ->authorizeIndividualRecords('delete')
    ->action(function (BulkAction $action, Collection $records) {
        $records->each(function (Model $record) use ($action) {
            $record->delete() || $action->reportBulkProcessingFailure(
                'deletion_failed',
                message: function (int $failureCount, int $totalCount): string {
                    if (($failureCount === 1) && ($totalCount === 1)) {
                        return 'One user failed to delete.';
                    }
        
                    if ($failureCount === $totalCount) {
                        return 'All users failed to delete.';
                    }
        
                    if ($failureCount === 1) {
                        return 'One of the selected users failed to delete.';
                    }
        
                    return "{$failureCount} of the selected users failed to delete.";
                },
            );
        });
    })
    ->successNotificationTitle('Deleted users')
    ->failureNotificationTitle(function (int $successCount, int $totalCount): string {
        if ($successCount) {
            return "{$successCount} of {$totalCount} users deleted";
        }

        return 'Failed to delete any users';
    })

Eloquent 模型上的 delete() 方法在删除失败时返回 false,因此可用它判断记录是否成功删除。随后 reportBulkProcessingFailure() 方法会向通知添加失败消息,并在操作完成时显示。

reportBulkProcessingFailure() 方法可在操作执行期间因不同原因在多个点调用,但对每条记录应只调用一次。对该记录调用该方法后,不应再继续处理该记录。

分组批量操作

你可以使用 BulkActionGroup 对象在下拉菜单中将多个批量操作分组。留在 BulkActionGroup 之外的批量操作会渲染在下拉触发按钮旁边:

php
use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            BulkActionGroup::make([
                BulkAction::make('delete')
                    ->requiresConfirmation()
                    ->action(fn (Collection $records) => $records->each->delete()),
                BulkAction::make('forceDelete')
                    ->requiresConfirmation()
                    ->action(fn (Collection $records) => $records->each->forceDelete()),
            ]),
            BulkAction::make('export')->button()->action(fn (Collection $records) => ...),
        ]);
}
带分组与未分组批量操作的表格带分组与未分组批量操作的表格

或者,若所有批量操作都已分组,可以使用简写方法 groupedBulkActions()

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

public function table(Table $table): Table
{
    return $table
        ->groupedBulkActions([
            BulkAction::make('delete')
                ->requiresConfirmation()
                ->action(fn (Collection $records) => $records->each->delete()),
            BulkAction::make('forceDelete')
                ->requiresConfirmation()
                ->action(fn (Collection $records) => $records->each->forceDelete()),
        ]);
}

批量操作完成后取消选中记录

你可以使用 deselectRecordsAfterCompletion() 方法在批量操作执行后取消选中记录:

php
use Filament\Actions\BulkAction;
use Illuminate\Database\Eloquent\Collection;

BulkAction::make('delete')
    ->action(fn (Collection $records) => $records->each->delete())
    ->deselectRecordsAfterCompletion()

对某些行禁用批量操作

你可以有条件地为特定记录禁用批量操作:

php
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            // ...
        ])
        ->checkIfRecordIsSelectableUsing(
            fn (Model $record): bool => $record->status === Status::Enabled,
        );
}

限制可选记录数量

你可以限制用户总共可选中多少条记录:

php
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Model;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            // ...
        ])
        ->maxSelectableRecords(4);
}

阻止批量选择所有页面

selectCurrentPageOnly() 方法可用于阻止用户一次性轻松批量选中表格中的所有记录,而只允许一次选择一页:

php
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            // ...
        ])
        ->selectCurrentPageOnly();
}

将批量选择限制为仅组内

selectGroupsOnly() 方法可将批量选择限制为同一分组内的记录,防止一次跨多个分组批量选择:

php
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            // ...
        ])
        ->selectGroupsOnly();
}

提升批量操作性能

默认情况下,批量操作会在将记录传给 action() 函数之前,将所有 Eloquent 记录加载到内存中。

若正在处理大量记录,可能希望使用 chunkSelectedRecords() 方法一次获取较少记录。这将降低应用的内存占用:

php
use Filament\Actions\BulkAction;
use Illuminate\Support\LazyCollection;

BulkAction::make()
    ->chunkSelectedRecords(250)
    ->action(function (LazyCollection $records) {
        // Process the records...
    })

你仍可像往常一样遍历 $records 集合,但该集合将是 LazyCollection 而非普通集合。

你也可以阻止 Filament 一开始就获取 Eloquent 模型,而只将选中记录的 ID 传给 action() 函数。若正在处理大量记录且无需将它们加载到内存,这会很有用:

php
use Filament\Actions\BulkAction;
use Illuminate\Support\Collection;

BulkAction::make()
    ->fetchSelectedRecords(false)
    ->action(function (Collection $records) {
        // Process the records...
    })

页眉操作

操作和批量操作都可以渲染在表格页眉中。可将它们放在 $table->headerActions() 方法中:

php
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->headerActions([
            // ...
        ]);
}

这对「创建」等与特定表格行无关的操作,或需要更醒目的批量操作很有用。

带页眉操作的表格带页眉操作的表格

工具栏操作

操作和批量操作都可以渲染在表格工具栏中。可将它们放在 $table->toolbarActions() 方法中:

php
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->toolbarActions([
            // ...
        ]);
}

这对「创建」等与特定表格行无关的操作,或需要更醒目的批量操作很有用。

带工具栏操作的表格带工具栏操作的表格

列操作

操作可添加到列上,使得点击该列中的单元格会触发操作。你可以在文档中了解更多关于列操作的信息。

分组操作

你可以使用 ActionGroup 对象将多个表格操作分组到下拉菜单中:

php
use Filament\Actions\ActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Tables\Table;

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

你可以在操作文档中了解更多关于自定义操作组的信息。

带操作组的表格带操作组的表格