Skip to content
全部文档

概述

简介

Resource 是用于为 Eloquent 模型构建 CRUD 界面的静态类。它们描述管理员应如何通过表格与表单与应用中的数据交互。

资源列表页面资源列表页面

创建资源

要为 App\Models\Customer 模型创建资源:

bash
php artisan make:filament-resource Customer

这会在 app/Filament/Resources 目录中创建若干文件:

.
+-- Customers
|   +-- CustomerResource.php
|   +-- Pages
|   |   +-- CreateCustomer.php
|   |   +-- EditCustomer.php
|   |   +-- ListCustomers.php
|   +-- Schemas
|   |   +-- CustomerForm.php
|   +-- Tables
|   |   +-- CustomersTable.php

新的资源类位于 CustomerResource.php

Pages 目录中的类用于自定义与资源交互的应用页面。它们都是完整页面的 Livewire 组件,可按需任意自定义。

Schemas 目录中的类用于定义资源的 表单infolist 内容。Tables 目录中的类用于构建资源的表格。

TIP

已经创建了资源,但导航菜单中没有出现?若有 模型策略,请确保 viewAny() 方法返回 true

简单(模态)资源

有时模型足够简单,你只想在一个页面上管理记录,并用模态创建、编辑和删除。要生成带模态的简单资源:

bash
php artisan make:filament-resource Customer --simple

资源会有一个「Manage」页面,即带模态的列表页。

此外,简单资源没有 getRelations() 方法,因为 关联管理器 只显示在编辑页与查看页,而简单资源没有这些页面。其余部分相同。

简单(模态)资源的创建模态简单(模态)资源的创建模态
简单(模态)资源的编辑模态简单(模态)资源的编辑模态

自动生成表单与表格

若想节省时间,Filament 可根据模型的数据库列,用 --generate 自动生成 表单表格

bash
php artisan make:filament-resource Customer --generate

处理软删除

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

bash
php artisan make:filament-resource Customer --soft-deletes

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

生成查看页

默认只为资源生成列表、创建与编辑页。若还需要 查看页,请使用 --view 标志:

bash
php artisan make:filament-resource Customer --view

指定自定义模型命名空间

默认情况下,Filament 假定模型位于 App\Models 目录。可用 --model-namespace 标志传入不同的模型命名空间:

bash
php artisan make:filament-resource Customer --model-namespace=Custom\\Path\\Models

在此例中,模型应位于 Custom\Path\Models\Customer。请注意命令中需要的双反斜杠 \\

这样在 生成资源 时,Filament 就能定位模型并读取数据库结构。

同时生成模型、迁移与工厂

若希望在搭建资源时节省时间,Filament 也可同时用任意组合的 --model--migration--factory 标志,为新资源生成模型、迁移与工厂:

bash
php artisan make:filament-resource Customer --model --migration --factory

记录标题

可为资源设置 $recordTitleAttribute,即模型上可用于与其他记录区分的列名。

例如,可以是博客文章的 title,或客户的 name

php
protected static ?string $recordTitleAttribute = 'name';

这是 全局搜索 等功能正常工作所必需的。

TIP

若单列不足以标识记录,可指定 Eloquent 访问器 的名称。

资源表单

资源类包含 form() 方法,用于构建 创建编辑 页上的表单。

默认情况下,Filament 会创建 form schema 文件,并在 form() 方法中引用。这是为了保持资源类整洁有序,否则它会变得很大:

php
use App\Filament\Resources\Customers\Schemas\CustomerForm;
use Filament\Schemas\Schema;

public static function form(Schema $schema): Schema
{
    return CustomerForm::configure($schema);
}

CustomerForm 类中,可定义表单的字段与布局:

php
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;

public static function configure(Schema $schema): Schema
{
    return $schema
        ->components([
            TextInput::make('name')->required(),
            TextInput::make('email')->email()->required(),
            // ...
        ]);
}

components() 方法用于定义表单结构,是按显示顺序排列的 字段布局组件 数组。

请参阅 Forms 文档中的 指南,了解如何用 Filament 构建表单。

TIP

若更希望直接在资源类中定义表单,也可以这样做,并完全删除 form schema 类:

php
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;

public static function form(Schema $schema): Schema
{
    return $schema
        ->components([
            TextInput::make('name')->required(),
            TextInput::make('email')->email()->required(),
            // ...
        ]);
}

根据当前操作隐藏组件

表单组件的 hiddenOn() 方法允许你根据当前页面或操作动态隐藏字段。

在此例中,我们在 edit 页面隐藏 password 字段:

php
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\Operation;

TextInput::make('password')
    ->password()
    ->required()
    ->hiddenOn(Operation::Edit),

或者,可用 visibleOn() 快捷方法,仅在某一页面或操作上显示字段:

php
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\Operation;

TextInput::make('password')
    ->password()
    ->required()
    ->visibleOn(Operation::Create),

资源表格

资源类包含 table() 方法,用于构建 列表页 上的表格。

默认情况下,Filament 会创建 table 文件,并在 table() 方法中引用。这是为了保持资源类整洁有序,否则它会变得很大:

php
use App\Filament\Resources\Customers\Tables\CustomersTable;
use Filament\Tables\Table;

public static function table(Table $table): Table
{
    return CustomersTable::configure($table);
}

CustomersTable 类中,可定义表格的列、筛选器与操作:

php
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;

public static function configure(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('name'),
            TextColumn::make('email'),
            // ...
        ])
        ->filters([
            Filter::make('verified')
                ->query(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at')),
            // ...
        ])
        ->recordActions([
            EditAction::make(),
        ])
        ->toolbarActions([
            BulkActionGroup::make([
                DeleteBulkAction::make(),
            ]),
        ]);
}

请参阅 表格 文档,了解如何添加表格列、筛选器、操作等。

TIP

若更希望直接在资源类中定义表格,也可以这样做,并完全删除 table 类:

php
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
    
public static function table(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('name'),
            TextColumn::make('email'),
            // ...
        ])
        ->filters([
            Filter::make('verified')
                ->query(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at')),
            // ...
        ])
        ->recordActions([
            EditAction::make(),
        ])
        ->toolbarActions([
            BulkActionGroup::make([
                DeleteBulkAction::make(),
            ]),
        ]);
}

自定义模型标签

每个资源都有一个从模型名自动生成的「模型标签」。例如,App\Models\Customer 模型的标签为 customer

该标签用于 UI 的多处,可用 $modelLabel 属性自定义:

php
protected static ?string $modelLabel = 'cliente';

或者,可用 getModelLabel() 定义动态标签:

php
public static function getModelLabel(): string
{
    return __('filament/resources/customer.label');
}

自定义复数模型标签

资源还有一个从模型标签自动生成的「复数模型标签」。例如,customer 标签会复数化为 customers

可用 $pluralModelLabel 属性自定义复数标签:

php
protected static ?string $pluralModelLabel = 'clientes';

或者,可在 getPluralModelLabel() 方法中设置动态复数标签:

php
public static function getPluralModelLabel(): string
{
    return __('filament/resources/customer.plural_label');
}

自动大写模型标签

默认情况下,Filament 会在部分 UI(例如页面标题、导航菜单与面包屑)中自动将模型标签的每个单词首字母大写。

若要为某个资源禁用此行为,可在资源中设置 $hasTitleCaseModelLabel

php
protected static bool $hasTitleCaseModelLabel = false;

资源导航项

Filament 会使用 复数标签 自动为资源生成导航菜单项。

若想自定义导航项标签,可使用 $navigationLabel 属性:

php
protected static ?string $navigationLabel = 'Mis Clientes';

或者,可在 getNavigationLabel() 方法中设置动态导航标签:

php
public static function getNavigationLabel(): string
{
    return __('filament/resources/customer.navigation_label');
}

设置资源导航图标

$navigationIcon 属性支持任意 Blade 组件名称。默认已安装 Heroicons。不过,你也可以创建自定义图标组件,或安装其他图标库。

php
use BackedEnum;

protected static string | BackedEnum | null $navigationIcon = 'heroicon-o-user-group';

或者,可在 getNavigationIcon() 方法中设置动态导航图标:

php
use BackedEnum;
use Illuminate\Contracts\Support\Htmlable;

public static function getNavigationIcon(): string | BackedEnum | Htmlable | null
{
    return 'heroicon-o-user-group';
}

排序资源导航项

$navigationSort 属性允许你指定导航项的列出顺序:

php
protected static ?int $navigationSort = 2;

或者,可在 getNavigationSort() 方法中设置动态导航项顺序:

php
public static function getNavigationSort(): ?int
{
    return 2;
}

分组资源导航项

可通过指定 $navigationGroup 属性对导航项分组:

php
use UnitEnum;

protected static string | UnitEnum | null $navigationGroup = 'Shop';

或者,可用 getNavigationGroup() 方法设置动态分组标签:

php
public static function getNavigationGroup(): ?string
{
    return __('filament/navigation.groups.shop');
}

将资源导航项归入其他项之下

可通过设置 $navigationParentItem 属性,将导航项作为其他项的子项。可通过父项的页面或资源类,或通过其标签来引用父项:

php
use App\Filament\Resources\Products\ProductsResource;
use UnitEnum;

protected static ?string $navigationParentItem = ProductsResource::class;

protected static string | UnitEnum | null $navigationGroup = 'Shop';

或者,可通过标签引用父项:

php
use UnitEnum;

protected static ?string $navigationParentItem = 'Products';

protected static string | UnitEnum | null $navigationGroup = 'Shop';

也可使用 getNavigationParentItem() 方法动态确定父项:

php
use App\Filament\Resources\Products\ProductsResource;

public static function getNavigationParentItem(): ?string
{
    return ProductsResource::class;
}

或者,可返回父项的标签:

php
public static function getNavigationParentItem(): ?string
{
    return __('filament/navigation.groups.shop.items.products');
}

父项与子项必须属于同一导航分组。若父项有导航分组,子项也必须定义该分组,否则无法正确识别父项。无论通过类还是标签引用父项,都适用此规则。

TIP

若需要这样的第三级导航,应考虑改用 集群(clusters)。集群是资源与 自定义页面 的逻辑分组,可共享独立的导航。

生成资源页面的 URL

Filament 在资源类上提供静态方法 getUrl(),用于生成资源及其内部特定页面的 URL。传统上你需要手动拼接 URL 或使用 Laravel 的 route() 辅助函数,但这些方式依赖于资源的 slug 或路由命名约定。

不带参数的 getUrl() 会生成指向资源 列表页 的 URL:

php
use App\Filament\Resources\Customers\CustomerResource;

CustomerResource::getUrl(); // /admin/customers

也可生成指向资源内特定页面的 URL。每个页面的名称是资源 getPages() 数组中的键。例如,生成指向 创建页 的 URL:

php
use App\Filament\Resources\Customers\CustomerResource;

CustomerResource::getUrl('create'); // /admin/customers/create

getPages() 中的某些页面使用如 record 这样的 URL 参数。要生成这些页面的 URL 并传入记录,应使用第二个参数:

php
use App\Filament\Resources\Customers\CustomerResource;

CustomerResource::getUrl('edit', ['record' => $customer]); // /admin/customers/edit/1

在此例中,$customer 可以是 Eloquent 模型对象,或 ID。

生成资源模态的 URL

若使用只有一个页面的 简单资源,这会特别有用。

要为资源表格中的操作生成 URL,应将 tableActiontableActionRecord 作为 URL 参数传入:

php
use App\Filament\Resources\Customers\CustomerResource;
use Filament\Actions\EditAction;

CustomerResource::getUrl(parameters: [
    'tableAction' => EditAction::getDefaultName(),
    'tableActionRecord' => $customer,
]); // /admin/customers?tableAction=edit&tableActionRecord=1

或者,若要为页面上的操作(例如页眉中的 CreateAction)生成 URL,可将其传入 action 参数:

php
use App\Filament\Resources\Customers\CustomerResource;
use Filament\Actions\CreateAction;

CustomerResource::getUrl(parameters: [
    'action' => CreateAction::getDefaultName(),
]); // /admin/customers?action=create

生成其他面板中资源的 URL

若应用中有多个面板,getUrl() 会在当前面板内生成 URL。也可通过向 panel 参数传入面板 ID,指明资源所属的面板:

php
use App\Filament\Resources\Customers\CustomerResource;

CustomerResource::getUrl(panel: 'marketing');

自定义资源 Eloquent 查询

在 Filament 中,对资源模型的每次查询都会从 getEloquentQuery() 方法开始。

因此,很容易应用影响整个资源的自定义查询约束或 模型作用域

php
public static function getEloquentQuery(): Builder
{
    return parent::getEloquentQuery()->where('is_active', true);
}

禁用全局作用域

默认情况下,Filament 会遵循模型上注册的所有全局作用域。不过,若希望访问例如已软删除的记录,这可能并不理想。

为此,可覆盖 Filament 使用的 getEloquentQuery() 方法:

php
public static function getEloquentQuery(): Builder
{
    return parent::getEloquentQuery()->withoutGlobalScopes();
}

或者,可移除特定的全局作用域:

php
public static function getEloquentQuery(): Builder
{
    return parent::getEloquentQuery()->withoutGlobalScopes([ActiveScope::class]);
}

关于移除全局作用域的更多信息,请参阅 Laravel 文档

自定义资源 URL

默认情况下,Filament 会根据资源名称生成 URL。可通过在资源上设置 $slug 属性自定义:

php
protected static ?string $slug = 'pending-orders';

资源子导航

子导航允许用户在资源内的不同页面之间导航。通常,子导航中的所有页面都与资源中的同一条记录相关。例如,在 Customer 资源中,你可能有如下子导航页面:

  • 查看客户:[`ViewRecord` 页面](/5.x/resources/viewing-records),只读展示客户详情。
  • 编辑客户:[`EditRecord` 页面](/5.x/resources/editing-records),允许用户编辑客户详情。
  • 编辑客户联系方式:[`EditRecord` 页面](/5.x/resources/editing-records),允许用户编辑客户联系方式。可 [了解如何创建多个编辑页](/5.x/resources/editing-records#creating-another-edit-page)。
  • 管理地址:[`ManageRelatedRecords` 页面](/5.x/resources/managing-relationships#relation-pages),允许用户管理客户地址。
  • 管理付款:[`ManageRelatedRecords` 页面](/5.x/resources/managing-relationships#relation-pages),允许用户管理客户付款。

要为资源中每个「单条记录」页面添加子导航,可在资源类上添加 getRecordSubNavigation() 方法:

php
use Filament\Resources\Pages\Page;

public static function getRecordSubNavigation(Page $page): array
{
    return $page->generateNavigationItems([
        ViewCustomer::class,
        EditCustomer::class,
        EditCustomerContact::class,
        ManageCustomerAddresses::class,
        ManageCustomerPayments::class,
    ]);
}

子导航中的每一项都可用 与普通页面相同的导航方法 自定义。

带有子导航的资源带有子导航的资源

TIP

若希望添加子导航以便在整个资源与 自定义页面 之间 切换,你可能需要的是 集群(clusters),用于将它们分组。getRecordSubNavigation() 用于构建资源 内部、与某条特定记录相关的页面之间的导航。

设置资源的子导航位置

子导航默认渲染在页面起始位置。可通过在资源上设置 $subNavigationPosition,更改该资源所有页面的位置。取值可为 SubNavigationPosition::StartSubNavigationPosition::End,或 SubNavigationPosition::Top(以标签页形式渲染子导航):

php
use Filament\Pages\Enums\SubNavigationPosition;

protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::End;
子导航位于末端的资源子导航位于末端的资源

SubNavigationPosition::Top 选项会将子导航渲染为页面内容上方的标签页:

子导航位于顶部的资源子导航位于顶部的资源

删除资源页面

若要从资源中删除某个页面,只需删除资源 Pages 目录中的页面文件,以及 getPages() 方法中的对应条目。

例如,你可能有一个不允许任何人创建记录的资源。删除 Create 页面文件,再从 getPages() 中移除它:

php
public static function getPages(): array
{
    return [
        'index' => ListCustomers::route('/'),
        'edit' => EditCustomer::route('/{record}/edit'),
    ];
}

删除页面不会删除链接到该页面的操作。这些操作会打开模态,而不是将用户送到不存在的页面。例如,列表页上的 CreateAction、表格或查看页上的 EditAction,或表格或编辑页上的 ViewAction。若要移除这些按钮,也必须删除这些操作。

安全

授权

对于授权,Filament 会遵循应用中已注册的任意 模型策略。使用以下方法:

  • viewAny() 用于从导航菜单中完全隐藏资源,并阻止用户访问任何页面。
  • create() 用于控制 创建新记录
  • update() 用于控制 编辑记录
  • view() 用于控制 查看记录
  • delete() 用于阻止删除单条记录。deleteAny() 用于阻止批量删除。Filament 使用 deleteAny(),因为逐条检查 delete() 策略性能不佳。使用 DeleteBulkAction 时,若仍想对每条记录调用 delete(),应使用 DeleteBulkAction::make()->authorizeIndividualRecords()。未通过授权检查的记录不会被处理。
  • forceDelete() 用于阻止强制删除单条已软删除记录。forceDeleteAny() 用于阻止批量强制删除。Filament 使用 forceDeleteAny(),因为逐条检查 forceDelete() 策略性能不佳。使用 ForceDeleteBulkAction 时,若仍想对每条记录调用 forceDelete(),应使用 ForceDeleteBulkAction::make()->authorizeIndividualRecords()。未通过授权检查的记录不会被处理。
  • restore() 用于阻止恢复单条已软删除记录。restoreAny() 用于阻止批量恢复。Filament 使用 restoreAny(),因为逐条检查 restore() 策略性能不佳。使用 RestoreBulkAction 时,若仍想对每条记录调用 restore(),应使用 RestoreBulkAction::make()->authorizeIndividualRecords()。未通过授权检查的记录不会被处理。
  • reorder() 用于控制 在表格中重排记录

跳过授权

若希望为资源跳过授权,可将 $shouldSkipAuthorization 属性设为 true

php
protected static bool $shouldSkipAuthorization = true;

保护模型属性

Filament 会将所有模型属性暴露给 JavaScript,除非它们在模型上被设为 $hidden。这是 Livewire 模型绑定的行为。我们保留此功能,以便在初始加载后动态增删表单字段,同时保留字段可能需要的数据。

DANGER

尽管属性可能在 JavaScript 中可见,但只有带表单字段的属性才真正可由用户编辑。这与批量赋值无关。

要在编辑页与查看页上将某些属性从 JavaScript 中移除,可覆盖 mutateFormDataBeforeFill() 方法

php
protected function mutateFormDataBeforeFill(array $data): array
{
    unset($data['is_admin']);

    return $data;
}

在此例中,我们将 is_admin 属性从 JavaScript 中移除,因为表单未使用它。

WARNING

当列包含非有效 UTF-8 的二进制数据(例如 geometrypointblob 列)时,将其加入 $hidden 是必须的,而不仅是建议。由于 Filament 会将模型属性暴露给 JavaScript,这些值会作为 Livewire 请求的一部分发送到浏览器,但无法序列化为 JSON。这会导致页面加载失败,常见表现为空白页,且 Laravel 日志中没有错误。

将这些列加入模型的 $hidden 数组,可使其从数组与 JSON 表示中排除,从而解决问题:

php
protected $hidden = ['location'];

若需要使用该值,请通过 访问器 暴露,而不是原始列。