Skip to content
全部文档

概述

简介

「Action」(操作)这个词在 Laravel 社区中经常出现。传统上,action PHP 类负责在应用业务逻辑中「做」某件事。例如:用户登录、发送邮件,或在数据库中创建新用户记录。

在 Filament 中,操作同样负责在应用中「做」某件事,但与传统 action 略有不同。它们被设计用于用户界面场景。例如,你可能有一个删除客户记录的按钮,点击后会打开模态框以确认决定。当用户在模态框中点击「Delete」按钮时,客户被删除。整个工作流就是一个「操作」。

php
use Filament\Actions\Action;

Action::make('delete')
    ->requiresConfirmation()
    ->action(fn () => $this->client->delete())

操作还可以向用户收集额外信息。例如,你可能有一个给客户发邮件的按钮。用户点击按钮后,会打开模态框以收集邮件主题和正文。当用户在模态框中点击「Send」按钮时,邮件即被发送:

php
use Filament\Actions\Action;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\TextInput;
use Illuminate\Support\Facades\Mail;

Action::make('sendEmail')
    ->schema([
        TextInput::make('subject')->required(),
        RichEditor::make('body')->required(),
    ])
    ->action(function (array $data) {
        Mail::to($this->client)
            ->send(new GenericEmail(
                subject: $data['subject'],
                body: $data['body'],
            ));
    })

TIP

除了 $data 之外,action() 函数还可以注入各种实用工具作为参数。

通常,操作会在不把用户重定向离开当前页面的情况下执行,因为我们大量使用 Livewire。不过,操作也可以更简单,甚至不需要模态框。你可以为操作传入一个 URL,当用户点击按钮时,就会跳转到该页面:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))

TIP

除了允许静态值外,url() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

DANGER

如果向 url() 方法传入用户可控的数据,你应当验证该 URL 未使用危险协议(例如 javascript:data:)。否则可能使应用暴露于 XSS 攻击。最简单的防护方式是用 Filament 的 Str::sanitizeUrl() 辅助方法包装该值;对于未使用 http/https(或相对路径)的 URL,它会返回 null

操作触发按钮和模态框的整体外观都可通过流畅的 PHP 方法自定义。我们为 UI 提供了合理且一致的样式,但你也可以用 CSS 全部自定义。

可用操作

Filament 内置了若干可直接加入应用的操作,旨在简化最常见的 Eloquent 相关操作:

你也可以创建自己的操作来完成任意事情;以上只是我们开箱提供的常见操作。

选择触发样式

开箱即用,操作触发器有 4 种样式:「button」、「link」、「icon button」和「badge」。

「Button」触发器带有背景色、标签,以及可选的 图标。通常这是默认按钮样式,但你也可以用 button() 方法手动指定:

php
use Filament\Actions\Action;

Action::make('edit')
    ->button()
按钮触发器按钮触发器

「Link」触发器没有背景色。它们必须有标签,并可选择带有 图标。外观类似嵌在文本中的链接。你可以用 link() 方法切换到该样式:

php
use Filament\Actions\Action;

Action::make('edit')
    ->link()
链接触发器链接触发器

「Icon button」触发器是带有 图标、无标签的圆形按钮。你可以用 iconButton() 方法切换到该样式:

php
use Filament\Actions\Action;

Action::make('edit')
    ->icon('heroicon-m-pencil-square')
    ->iconButton()
图标按钮触发器图标按钮触发器

「Badge」触发器带有背景色、标签,以及可选的 图标。你可以使用 badge() 方法将徽章用作触发器:

php
use Filament\Actions\Action;

Action::make('edit')
    ->badge()
徽章触发器徽章触发器

仅在移动设备上使用图标按钮

你可能希望在桌面上使用带标签的按钮样式,而在移动端去掉标签,从而变成图标按钮。这可以通过 labeledFrom() 方法实现,并传入希望开始显示标签的响应式 断点

php
use Filament\Actions\Action;

Action::make('edit')
    ->icon('heroicon-m-pencil-square')
    ->button()
    ->labeledFrom('md')

设置标签

默认情况下,触发按钮的标签由其名称生成。你可以使用 label() 方法自定义:

php
use Filament\Actions\Action;

Action::make('edit')
    ->label('Edit post')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))

TIP

除了允许静态值外,label() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

设置颜色

按钮可以设置 颜色 以表明其重要程度:

php
use Filament\Actions\Action;

Action::make('delete')
    ->color('danger')

TIP

除了允许静态值外,color() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

红色触发器红色触发器

设置尺寸

按钮有 3 种尺寸:Size::SmallSize::MediumSize::Large。你可以使用 size() 方法更改操作触发器的尺寸:

php
use Filament\Actions\Action;
use Filament\Support\Enums\Size;

Action::make('create')
    ->size(Size::Large)

TIP

除了允许静态值外,size() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

大尺寸触发器大尺寸触发器

设置图标

按钮可以带有 图标,以丰富 UI 细节。你可以使用 icon() 方法设置图标:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->icon('heroicon-m-pencil-square')

TIP

除了允许静态值外,icon() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

带图标的触发器带图标的触发器

你还可以使用 iconPosition() 方法将图标位置改到标签之后,而不是之前:

php
use Filament\Actions\Action;
use Filament\Support\Enums\IconPosition;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->icon('heroicon-m-pencil-square')
    ->iconPosition(IconPosition::After)

TIP

除了允许静态值外,iconPosition() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

图标在标签后的触发器图标在标签后的触发器

授权

你可以根据条件对特定用户显示或隐藏操作。为此,可使用 visible()hidden() 方法:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->visible(auth()->user()->can('update', $this->post))

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->hidden(! auth()->user()->can('update', $this->post))

这非常适合将某些操作仅授权给有权限的用户。

TIP

除了允许静态值外,visible()hidden() 方法也接受函数来动态计算值。你可以将各种实用工具作为参数注入这些函数。

使用策略授权

你可以使用策略来授权操作。为此,将策略方法名传给 authorize() 方法,Filament 会使用该操作的当前 Eloquent 模型查找正确的策略:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->authorize('update')

INFO

若在面板资源或关联管理器中使用操作,则无需使用 authorize() 方法,因为 Filament 会根据资源模型自动读取策略,适用于 CreateActionEditActionDeleteAction 等内置操作。更多信息请参阅 资源授权 部分。

若策略方法返回 响应消息,你可以使用 authorizationTooltip() 方法禁用该操作(而不是隐藏它),并添加包含该消息的提示:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->authorize('update')
    ->authorizationTooltip()

若拒绝时未提供消息(例如策略直接返回 false,或 Gate::before() 钩子短路了检查),则会改为隐藏该操作。此时可用 authorizationMessage() 提供后备消息,以保持操作可见。

带授权提示的禁用操作按钮带授权提示的禁用操作按钮

你也可以使用 authorizationNotification() 方法,让未授权用户仍可点击该操作,但会发送包含响应消息的通知:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->authorize('update')
    ->authorizationNotification()

authorizationTooltip() 一样,若拒绝时未提供消息,则会隐藏该操作,除非你用 authorizationMessage() 提供后备消息。

禁用按钮

若要禁用按钮而不是隐藏它,可以使用 disabled() 方法:

php
use Filament\Actions\Action;

Action::make('delete')
    ->disabled()

你可以通过传入布尔值来按条件禁用按钮:

php
use Filament\Actions\Action;

Action::make('delete')
    ->disabled(! auth()->user()->can('delete', $this->post))

TIP

除了允许静态值外,disabled() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

禁用的操作按钮禁用的操作按钮

注册快捷键

你可以为触发按钮绑定键盘快捷键。它们使用与 Mousetrap 相同的按键代码:

php
use Filament\Actions\Action;

Action::make('save')
    ->action(fn () => $this->save())
    ->keyBindings(['command+s', 'ctrl+s'])

TIP

除了允许静态值外,keyBindings() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

在按钮角落添加徽章

你可以在按钮角落添加徽章以显示任意内容。这对展示计数或状态指示很有用:

php
use Filament\Actions\Action;

Action::make('filter')
    ->iconButton()
    ->icon('heroicon-m-funnel')
    ->badge(5)

TIP

除了允许静态值外,badge() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

带徽章的触发器带徽章的触发器

你还可以为徽章传入 颜色

php
use Filament\Actions\Action;

Action::make('filter')
    ->iconButton()
    ->icon('heroicon-m-funnel')
    ->badge(5)
    ->badgeColor('success')

TIP

除了允许静态值外,badgeColor() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

带绿色徽章的触发器带绿色徽章的触发器

描边按钮样式

当使用「button」触发样式时,你可能希望让它不那么醒目。你可以换用不同的 颜色,但有时你可能更希望使用描边样式。这可以通过 outlined() 方法实现:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->button()
    ->outlined()
描边触发按钮描边触发按钮

可选地,你可以传入布尔值来控制标签是否隐藏:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->button()
    ->outlined(FeatureFlag::active())

TIP

除了允许静态值外,outlined() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

为操作添加额外 HTML 属性

你可以通过 extraAttributes() 方法向操作传入额外 HTML 属性,这些属性会合并到其外层 HTML 元素上。属性应以数组表示,键为属性名,值为属性值:

php
use Filament\Actions\Action;

Action::make('edit')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))
    ->extraAttributes([
        'title' => 'Edit this post',
    ])

TIP

除了允许静态值外,extraAttributes() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

TIP

默认情况下,多次调用 extraAttributes() 会覆盖先前的属性。若希望改为合并属性,可以向该方法传入 merge: true

在 schema 中使用操作

Action 对象可以插入到 schema 中的任意位置,例如 表单字段插槽分区页眉与页脚,或与 prime 组件 并列。当操作在 schema 中使用时,可通过 实用工具注入 访问 schema 的状态——你可以在闭包中使用 $schemaGet$schemaSet 读取和修改表单字段值。

php
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;

TextInput::make('title')
    ->afterContent(
        Action::make('generateSlug')
            ->action(function (Get $schemaGet, Set $schemaSet) {
                $schemaSet('slug', str($schemaGet('title'))->slug());
            })
    )

TextInput::make('slug')

向 schema 添加操作列表

若要在 schema 中单独一行渲染一组操作按钮,而不将它们绑定到特定字段,可以将它们包裹在 Actions 布局组件中:

php
use Filament\Actions\Action;
use Filament\Schemas\Components\Actions;

Actions::make([
    Action::make('star')
        ->icon('heroicon-m-star'),
    Action::make('resetStars')
        ->icon('heroicon-m-x-mark')
        ->color('danger'),
])
schema 中的独立操作schema 中的独立操作

你可以使用 fullWidth() 方法让这些操作占满 schema 的完整宽度:

php
use Filament\Actions\Action;
use Filament\Schemas\Components\Actions;

Actions::make([
    Action::make('star')
        ->icon('heroicon-m-star'),
    Action::make('resetStars')
        ->icon('heroicon-m-x-mark')
        ->color('danger'),
])->fullWidth()
schema 中的全宽独立操作schema 中的全宽独立操作

你可以使用 alignment() 方法更改操作的水平对齐方式:

php
use Filament\Actions\Action;
use Filament\Schemas\Components\Actions;
use Filament\Support\Enums\Alignment;

Actions::make([
    Action::make('star')
        ->icon('heroicon-m-star'),
    Action::make('resetStars')
        ->icon('heroicon-m-x-mark')
        ->color('danger'),
])->alignment(Alignment::Center)
schema 中居中对齐的独立操作schema 中居中对齐的独立操作

Actions 组件与其他组件一起处于网格中,你可以使用 verticalAlignment() 方法更改其垂直对齐方式:

php
use Filament\Actions\Action;
use Filament\Schemas\Components\Actions;
use Filament\Support\Enums\VerticalAlignment;

Actions::make([
    Action::make('star')
        ->icon('heroicon-m-star'),
    Action::make('resetStars')
        ->icon('heroicon-m-x-mark')
        ->color('danger'),
])->verticalAlignment(VerticalAlignment::End)
schema 中垂直对齐到末尾的独立操作schema 中垂直对齐到末尾的独立操作

点击操作时运行 JavaScript

若需要一个直接在浏览器中运行 JavaScript、而不发起网络请求的简单操作,可以使用 actionJs() 方法。这对即时更新表单字段值等简单交互很有用:

php
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;

TextInput::make('title')
    ->live(onBlur: true)
    ->afterContent(
        Action::make('generateSlug')
            ->actionJs(<<<'JS'
                $set('slug', $get('title').toLowerCase().replaceAll(' ', '-'))
                JS)
    )

TextInput::make('slug')

该 JavaScript 字符串可以使用 $get()$set() 实用工具,从而读取和修改 schema 中表单字段的状态。

TIP

除了允许静态值外,actionJs() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

WARNING

使用 actionJs() 时,该操作无法打开模态框,也无法执行任何服务端处理。它仅适用于简单的客户端交互。若需要运行 PHP 代码,请改用 action() 方法。

DANGER

传入 actionJs() 方法的任何 JavaScript 字符串都会在浏览器中执行,因此绝不要把用户输入直接加入该字符串,否则可能导致跨站脚本(XSS)漏洞。来自 $get() 的用户输入绝不应作为 JavaScript 代码求值,但作为字符串值使用是安全的。

操作实用工具注入

绝大多数用于配置操作的方法都接受函数作为参数,而不是硬编码值:

php
use Filament\Actions\Action;

Action::make('edit')
    ->label('Edit post')
    ->url(fn (): string => route('posts.edit', ['post' => $this->post]))

仅此一点就解锁了许多自定义可能。

该包还可以将这些实用工具作为参数注入到这些函数中。所有接受函数参数的自定义方法都可以注入实用工具。

这些被注入的实用工具要求使用特定的参数名,否则 Filament 不知道该注入什么。

注入当前模态表单数据

若要访问当前的 模态表单数据,请定义 $data 参数:

php
function (array $data) {
    // ...
}

请注意,若模态框尚未提交,该值将为空。

注入 Eloquent 记录

若操作关联了 Eloquent 记录(例如位于表格行上),你可以使用 $record 参数注入该记录:

php
use Illuminate\Database\Eloquent\Model;

function (Model $record) {
    // ...
}

注入当前参数

若要访问已传给操作的 当前参数,请定义 $arguments 参数:

php
function (array $arguments) {
    // ...
}

从 schema 注入实用工具

若操作定义在 schema 中,还可以访问各种额外实用工具:

  • `$schema` - 操作所属的 schema 实例。
  • `$schemaComponent` - 操作所属的 schema 组件实例。
  • `$schemaComponentState` - schema 组件的当前值。
  • `$schemaState` - 该操作所属 schema 的当前值,例如当前的 repeater 项。
  • `$schemaGet` - 用于从 schema 数据中检索值的函数。不会对表单字段运行验证。
  • `$schemaSet` - 用于在 schema 数据中设置值的函数。
  • `$schemaOperation` - schema 当前执行的操作。通常是 `create`、`edit` 或 `view`。

更多信息请参阅 Schemas 部分

注入当前 Livewire 组件实例

若要访问操作所属的当前 Livewire 组件实例,请定义 $livewire 参数:

php
use Livewire\Component;

function (Component $livewire) {
    // ...
}

注入当前操作实例

若要访问当前操作实例,请定义 $action 参数:

php
function (Action $action) {
    // ...
}

注入多个实用工具

这些参数通过反射动态注入,因此你可以按任意顺序组合多个参数:

php
use Livewire\Component;

function (array $arguments, Component $livewire) {
    // ...
}

从 Laravel 容器注入依赖

你可以像往常一样从 Laravel 容器注入任意内容,并与实用工具一起使用:

php
use Illuminate\Http\Request;

function (Request $request, array $arguments) {
    // ...
}

操作速率限制

你可以使用 rateLimit() 方法对操作进行速率限制。该方法接受每个用户 IP 地址每分钟允许的尝试次数。若用户超出限制,操作将不会运行,并会显示通知:

php
use Filament\Actions\Action;

Action::make('delete')
    ->rateLimit(5)

若操作会打开模态框,则速率限制会在模态框提交时应用。

若操作带着参数打开,或针对特定 Eloquent 记录打开,则速率限制会按每个操作的参数或记录的唯一组合分别生效。速率限制对面板中当前的 Livewire 组件/页面也是唯一的。

TIP

除了允许静态值外,rateLimit() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

自定义达到速率限制时的通知

当操作达到速率限制时,会向用户发送通知,提示已触发速率限制。

若要自定义该通知的标题,请使用 rateLimitedNotificationTitle() 方法:

php
use Filament\Actions\DeleteAction;

DeleteAction::make()
    ->rateLimit(5)
    ->rateLimitedNotificationTitle('Slow down!')

TIP

除了允许静态值外,rateLimitedNotificationTitle() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

你可以使用 rateLimitedNotification() 方法自定义整个通知:

php
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use Filament\Actions\DeleteAction;
use Filament\Notifications\Notification;

DeleteAction::make()
    ->rateLimit(5)
    ->rateLimitedNotification(
       fn (TooManyRequestsException $exception): Notification => Notification::make()
            ->warning()
            ->title('Slow down!')
            ->body("You can try deleting again in {$exception->secondsUntilAvailable} seconds."),
    )

TIP

除了允许静态值外,rateLimitedNotification() 方法也接受一个函数来动态计算值。你可以将各种实用工具作为参数注入该函数。

自定义速率限制行为

若要自定义速率限制行为,可以在操作中结合使用 Laravel 的 速率限制 功能与 Filament 的 闪存通知

若要在操作模态框打开时立即进行速率限制,可以在 mountUsing() 方法中完成:

php
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Illuminate\Support\Facades\RateLimiter;

Action::make('delete')
    ->mountUsing(function () {
        if (RateLimiter::tooManyAttempts(
            $rateLimitKey = 'delete:' . auth()->id(),
            maxAttempts: 5,
        )) {
            Notification::make()
                ->title('Too many attempts')
                ->body('Please try again in ' . RateLimiter::availableIn($rateLimitKey) . ' seconds.')
                ->danger()
                ->send();

            return;
        }

         RateLimiter::hit($rateLimitKey);
    })

若要在操作运行时进行速率限制,可以在 action() 方法中完成:

php
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Illuminate\Support\Facades\RateLimiter;

Action::make('delete')
    ->action(function () {
        if (RateLimiter::tooManyAttempts(
            $rateLimitKey = 'delete:' . auth()->id(),
            maxAttempts: 5,
        )) {
            Notification::make()
                ->title('Too many attempts')
                ->body('Please try again in ' . RateLimiter::availableIn($rateLimitKey) . ' seconds.')
                ->danger()
                ->send();

            return;
        }

         RateLimiter::hit($rateLimitKey);

        // ...
    })