面板配置
简介
认识面板
不过,你可以创建任意数量的面板,每个面板都可以拥有自己的资源、页面和小部件。
例如,你可以构建一个面板,让用户在 /app 登录并访问仪表盘,管理员在 /admin 登录并管理应用。/app 面板和 /admin 面板各自拥有资源,因为每组用户的需求不同。Filament 通过支持创建多个面板来实现这一点。
默认 admin 面板
运行 filament:install 时,会在 app/Providers/Filament 中创建新文件 AdminPanelProvider.php。该文件包含 /admin 面板的配置。
本文档提到「配置」时,指的就是需要编辑的这个文件。它允许你完全自定义应用。
创建新面板
若要创建新面板,可以使用 make:filament-panel 命令,并传入新面板的唯一名称:
php artisan make:filament-panel app此命令会创建一个名为「app」的新面板。配置文件会创建在 app/Providers/Filament/AppPanelProvider.php。你可以在 /app 访问该面板,也可以 自定义路径。
由于此配置文件同时也是 Laravel 服务提供者,需要在 bootstrap/providers.php(Laravel 11 及以上的应用结构)或 config/app.php(Laravel 10 及以下的应用结构)中注册。Filament 会尝试替你完成注册,但若访问面板时报错,则该过程可能失败了。
更改路径
在面板配置文件中,可以使用 path() 方法更改应用的访问路径:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->path('app');
}若希望应用无需任何前缀即可访问,可以将其设为空字符串:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->path('');
}请确保 routes/web.php 尚未定义 '' 或 '/' 路由,否则它们会优先生效。
渲染钩子
渲染钩子 允许你在框架视图的各个位置渲染 Blade 内容。你可以在服务提供者或中间件中 注册全局渲染钩子,也可以注册仅针对某个面板的渲染钩子。为此,可以在面板配置对象上使用 renderHook() 方法。下面是一个将 wire-elements/modal 与 Filament 集成的示例:
use Filament\Panel;
use Filament\View\PanelsRenderHook;
use Illuminate\Support\Facades\Blade;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->renderHook(
PanelsRenderHook::BODY_START,
fn (): string => Blade::render('@livewire(\'livewire-ui-modal\')'),
);
}可用渲染钩子的完整列表见 此处。
设置域名
默认情况下,Filament 会响应来自所有域名的请求。若要将其限定到特定域名,可以使用 domain() 方法,类似于 Laravel 中的 Route::domain():
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->domain('admin.example.com');
}自定义最大内容宽度
默认情况下,Filament 会限制页面内容宽度,以免在大屏幕上过宽。若要更改,可以使用 maxContentWidth() 方法。选项对应 Tailwind 的 max-width 比例。可选值为 ExtraSmall、Small、Medium、Large、ExtraLarge、TwoExtraLarge、ThreeExtraLarge、FourExtraLarge、FiveExtraLarge、SixExtraLarge、SevenExtraLarge、Full、MinContent、MaxContent、FitContent、Prose、ScreenSmall、ScreenMedium、ScreenLarge、ScreenExtraLarge 和 ScreenTwoExtraLarge。默认为 SevenExtraLarge:
use Filament\Panel;
use Filament\Support\Enums\Width;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->maxContentWidth(Width::Full);
}

若要为 SimplePage 类型的页面(如登录和注册页)设置最大内容宽度,可以使用 simplePageMaxContentWidth() 方法。默认为 Large:
use Filament\Panel;
use Filament\Support\Enums\Width;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->simplePageMaxContentWidth(Width::Small);
}

设置默认子导航位置
子导航默认渲染在每个页面的起始位置。可以按页面、资源和集群自定义,也可以使用 subNavigationPosition() 方法一次性为整个面板自定义。取值可以是 SubNavigationPosition::Start、SubNavigationPosition::End,或 SubNavigationPosition::Top(将子导航渲染为标签页):
use Filament\Pages\Enums\SubNavigationPosition;
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->subNavigationPosition(SubNavigationPosition::End);
}生命周期钩子
钩子可用于在面板生命周期中执行代码。bootUsing() 会在该面板内的每次请求时运行。若有多个面板,仅会运行当前面板的 bootUsing()。该函数从中间件中运行,时机是在所有服务提供者启动之后:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->bootUsing(function (Panel $panel) {
// ...
});
}SPA 模式
SPA 模式利用 Livewire 的 wire:navigate 功能,让服务端渲染的面板感觉像单页应用:页面切换延迟更小,较长请求会显示加载条。要在面板上启用 SPA 模式,可以使用 spa() 方法:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->spa();
}为特定 URL 禁用 SPA 导航
默认情况下,启用 SPA 模式后,与当前请求位于同一域名的任何 URL 都会使用 Livewire 的 wire:navigate 进行导航。若要为特定 URL 禁用此行为,可以使用 spaUrlExceptions() 方法:
use App\Filament\Resources\Posts\PostResource;
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->spa()
->spaUrlExceptions(fn (): array => [
url('/admin'),
PostResource::getUrl(),
]);
}INFO
本例中,我们在资源上使用 getUrl() 获取资源索引页的 URL。此功能需要面板已经注册,而配置发生在请求生命周期中过早的阶段,无法做到这一点。你可以使用函数返回 URL,它们会在面板注册后再解析。
这些 URL 需要与用户正在导航到的 URL 完全匹配,包括域名和协议。若要用模式匹配多个 URL,可以使用星号(*)作为通配符:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->spa()
->spaUrlExceptions([
'*/admin/posts/*',
]);
}启用 SPA 预取
SPA 预取会在用户悬停链接时自动预取页面,使导航感觉更加灵敏。此功能利用 Livewire 的 wire:navigate.hover 能力。
若要启用带预取的 SPA 模式,可以向 spa() 方法传入 hasPrefetching: true 参数:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->spa(hasPrefetching: true);
}启用预取后,面板内的所有链接都会自动包含 wire:navigate.hover,在用户悬停链接时预取页面内容。这与 URL 例外 无缝配合——从 SPA 模式排除的任何 URL 也会从预取中排除。
INFO
预取仅在启用 SPA 模式时生效。若禁用 SPA 模式,预取也会自动禁用。
WARNING
预取较重的页面会增加带宽占用和服务器负载,尤其是用户快速悬停大量链接时。请谨慎使用此功能,特别是应用中有包含大量数据或复杂查询的页面时。
未保存更改提醒
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->unsavedChangesAlerts();
}禁用了 schema 的操作模态框(例如只读的 ViewAction 模态框)不会触发提醒。你也可以使用 unsavedChangesAlert(false) 方法,为另一个不可能包含未保存更改的操作禁用提醒。
启用数据库事务
默认情况下,Filament 不会将操作包裹在数据库事务中,而是让用户在测试确认操作可以安全地包裹在事务中之后自行启用。不过,你可以使用 databaseTransactions() 方法一次性为所有操作启用数据库事务:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->databaseTransactions();
}对于不想包裹在事务中的操作,可以使用 databaseTransaction(false) 方法:
CreateAction::make()
->databaseTransaction(false)use Filament\Resources\Pages\CreateRecord;
class CreatePost extends CreateRecord
{
protected ?bool $hasDatabaseTransactions = false;
// ...
}为特定操作和页面选择加入数据库事务
除了在所有地方启用数据库事务再为特定操作和页面退出之外,你也可以仅为特定操作和页面选择加入数据库事务。
对于操作,可以使用 databaseTransaction() 方法:
CreateAction::make()
->databaseTransaction()use Filament\Resources\Pages\CreateRecord;
class CreatePost extends CreateRecord
{
protected ?bool $hasDatabaseTransactions = true;
// ...
}为面板注册资源
你可以注册仅在特定面板页面中加载、而不会在应用其余部分加载的 资源。为此,将资源数组传给 assets() 方法:
use Filament\Panel;
use Filament\Support\Assets\Css;
use Filament\Support\Assets\Js;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->assets([
Css::make('custom-stylesheet', resource_path('css/custom.css')),
Js::make('custom-script', resource_path('js/custom.js')),
]);
}在这些 资源 可以使用之前,需要运行 php artisan filament:assets。
应用中间件
你可以通过在配置中将中间件类数组传给 middleware() 方法,为所有路由应用额外中间件:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->middleware([
// ...
]);
}默认情况下,中间件会在页面首次加载时运行,但不会在后续 Livewire AJAX 请求中运行。若要在每次请求都运行中间件,可以将 true 作为第二个参数传给 middleware() 方法,使其持久化:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->middleware([
// ...
], isPersistent: true);
}为已认证路由应用中间件
你可以通过在配置中将中间件类数组传给 authMiddleware() 方法,为所有已认证路由应用中间件:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->authMiddleware([
// ...
]);
}默认情况下,中间件会在页面首次加载时运行,但不会在后续 Livewire AJAX 请求中运行。若要在每次请求都运行中间件,可以将 true 作为第二个参数传给 authMiddleware() 方法,使其持久化:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->authMiddleware([
// ...
], isPersistent: true);
}禁用广播
默认情况下,若已在 已发布的 config/filament.php 配置文件 中设置凭据,Laravel Echo 会为每个面板自动连接。要在某个面板中禁用此自动连接,可以使用 broadcasting(false) 方法:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->broadcasting(false);
}严格授权模式
默认情况下,Filament 授权用户访问资源时,会先检查该模型是否存在策略,若存在,再检查策略上是否有执行该操作的方法。若策略或策略方法不存在,会授予用户对该资源的访问权限,因为它假定你尚未设置授权,或不需要授权。
若希望在策略或策略方法不存在时由 Filament 抛出异常,可以使用 strictAuthorization() 方法启用严格授权模式:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->strictAuthorization();
}配置错误通知
当 Laravel 的调试模式关闭时,Filament 会用更整洁的闪现通知替换 Livewire 的全屏错误模态框。你可以使用 errorNotifications(false) 方法禁用此行为:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->errorNotifications(false);
}你可以通过向 registerErrorNotification() 方法的 title 和 body 参数传入字符串,自定义错误通知文本:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->registerErrorNotification(
title: 'An error occurred',
body: 'Please try again later.',
);
}你也可以通过在 statusCode 参数中传入 HTTP 状态码(例如 404),为特定状态码注册错误通知文本:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->registerErrorNotification(
title: 'An error occurred',
body: 'Please try again later.',
)
->registerErrorNotification(
title: 'Record not found',
body: 'A record you are looking for does not exist.',
statusCode: 404,
);
}你也可以选择通过将 HTTP 状态码(例如 403)传给 hiddenErrorNotification() 方法来隐藏该状态码的通知。被隐藏的状态码仍会被 Filament 捕获,但不会显示通知。
或者,你可以使用 disabledErrorNotification() 方法,让该状态码回退到 Livewire 的内置错误处理。这在你想接入 Livewire 错误处理系统、为特定状态码自定义错误处理行为,同时为其他情况保留 Filament 错误通知系统时很有用。
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->registerErrorNotification(
title: 'An error occurred',
body: 'Please try again later.',
)
->hiddenErrorNotification(403)
->disabledErrorNotification(503);
}你也可以通过在页面类上设置 $hasErrorNotifications 属性,为面板中的特定页面启用或禁用错误通知:
use Filament\Pages\Dashboard as BaseDashboard;
class Dashboard extends BaseDashboard
{
protected ?bool $hasErrorNotifications = true;
// or
protected ?bool $hasErrorNotifications = false;
// ...
}若要运行自定义代码来检查是否应显示错误通知,可以在页面类上使用 hasErrorNotifications() 方法:
use Filament\Pages\Dashboard as BaseDashboard;
class Dashboard extends BaseDashboard
{
public function hasErrorNotifications(): bool
{
return FeatureFlag::active();
}
// ...
}你也可以在 setUpErrorNotifications() 方法中调用页面类上的 registerErrorNotification() 方法来注册错误通知文本:
use Filament\Pages\Dashboard as BaseDashboard;
class Dashboard extends BaseDashboard
{
protected function setUpErrorNotifications(): void
{
$this->registerErrorNotification(
title: 'An error occurred',
body: 'Please try again later.',
);
}
// ...
}你也可以通过在 statusCode 参数中传入 HTTP 状态码(例如 404),为特定状态码注册错误通知文本:
use Filament\Pages\Dashboard as BaseDashboard;
class Dashboard extends BaseDashboard
{
protected function setUpErrorNotifications(): void
{
$this->registerErrorNotification(
title: 'An error occurred',
body: 'Please try again later.',
);
$this->registerErrorNotification(
title: 'Record not found',
body: 'A record you are looking for does not exist.',
statusCode: 404,
);
}
// ...
}你也可以选择通过将 HTTP 状态码(例如 403)传给 hiddenErrorNotification() 方法来隐藏该状态码的通知。被隐藏的状态码仍会被 Filament 捕获,但不会显示通知。
或者,你可以使用 disabledErrorNotification() 方法,让该状态码回退到 Livewire 的内置错误处理。这在你想接入 Livewire 错误处理系统、为特定状态码自定义错误处理行为,同时为其他情况保留 Filament 错误通知系统时很有用。
use Filament\Pages\Dashboard as BaseDashboard;
class Dashboard extends BaseDashboard
{
protected function setUpErrorNotifications(): void
{
$this->registerErrorNotification(
title: 'An error occurred',
body: 'Please try again later.',
);
$this->hiddenErrorNotification(403);
$this->disabledErrorNotification(503);
}
// ...
}