面板构建器 - 资源
开始
概述
资源是用来为 Eloquent 模型创建 CRUD 接口的静态类。它描述了管理员如何在应用内,使用表格和表单,与数据进行交互的。
新建资源
为 App\Models\Customer
模型新建资源:
php artisan make:filament-resource Customer
在 app/Filament/Resources
目录下将会生成一系列文件:
.+-- CustomerResource.php+-- CustomerResource| +-- Pages| | +-- CreateCustomer.php| | +-- EditCustomer.php| | +-- ListCustomers.php
新的资源类在 CustomerResource.php
中。
Pages
目录下的类用于自定义应用中和资源交互的页面。这些页面是全页 Livewire 组件,你可以根据需要自定义。
如果你创建的资源并没有在导航菜单中显示,很可能是因为你的模型策略的
viewAny()
方法并没有返回true
。
简易(模态框)资源
有时候,你的模型过于简单,因此你只想在单个页面中管理记录。这种情况下你可以使用模态框进行创建,编辑和删除记录。要生成这样一个带有模态框的简易资源:
php artisan make:filament-resource Customer --simple
你的资源有一个管理(Manage)页面,该页面是一个带有模态框的列表页。
另外,你的资源类不再会有 getRelations()
方法,因为关系管理器只会在编辑页和查看页中展示,不会出现在简易资源中。其他方面则与普通资源相同。
自动生成表单和表格
如果你要节约时间,Filament 也可以基于你模型数据库的字段,为你自动生成一些表单和表格,请使用 --generate
:
php artisan make:filament-resource Customer --generate
如果数据库表中含有 ENUM 字段,
doctrine/dbal
包将无法扫描表格且会崩溃。因此 Filament 无法据此为资源生成 schema。更多详情查阅此处。
处理软删除
默认情况下,你不能在应用中和已删除的记录进行交互。如果你想要在资源中添加恢复(restore)、强制删除和过滤垃圾记录等功能,可以在生成资源的时候使用 --soft-deletes
标志:
php artisan make:filament-resource Customer --soft-deletes
你点击此处查阅更多关于软删除功能。
生成查看页面
默认情况下,资源只会生成列表页,新建页和编辑页。如果你也想要生成查看页,请使用 --view
标志:
php artisan make:filament-resource Customer --view
指定自定义模型命名空间
默认情况下,Filament 会假定你的模型在 App\Models
目录中。你可以使用 --model-namspace
标志,传入不同的模型命名空间:
php artisan make:filament-resource Customer --model-namespace=Custom\\Path\\Models
本例中,模型在 Custom\Path\Models\Customer
。请注意命令行中要求使用双反斜杆 \\
。
生成资源时,Filament 将能够定位模型并读取数据库 schema。
记录标题
你可以为资源设置 $recordTitleAttribute
属性,该属性对应模型上的字段名,可以用于将其与其他模型区别开来。(译者:此处直译,意思是大体是在搜索时用以确认某条记录的唯一性。)
比如,它可以是博客文章的标题(title)
或者客户的名字(name)
:
protected static ?string $recordTitleAttribute = 'name';
此属性需要启用全局搜索特性才有效。
如果某个单一字段无法有效识别一条记录,你可以指定一个 Eloquent 获取器的名称。
资源表单
资源类中包含着一个 form()
方法,用于为新建和编辑页面创建表单:
use Filament\Forms;use Filament\Forms\Form; public static function form(Form $form): Form{ return $form ->schema([ Forms\Components\TextInput::make('name')->required(), Forms\Components\TextInput::make('email')->email()->required(), // ... ]);}
schema()
方法用于定义表单结构。它是一个表单控件和布局组件数组,按照显示顺序在表单中排序。
关于如何使用 Filament 创建表单,请查阅表单文档向导。
基于当前操作中隐藏组件
表单组件的 hiddenOn()
方法让你可以基于当前页面或操作动态隐藏字段组件。
此例中,我们在 edit
编辑页面中隐藏了 password
字段:
use Livewire\Component; Forms\Components\TextInput::make('password') ->password() ->required() ->hiddenOn('edit'),
此外,还有一个 visibleOn()
快捷方法,用来在页面或 Action 中显示字段:
use Livewire\Component; Forms\Components\TextInput::make('password') ->password() ->required() ->visibleOn('create'),
资源表格
Resource 类包含一个静态 table()
方法,用以在列表页中创建表格:
use Filament\Tables;use Filament\Tables\Table;use Illuminate\Database\Eloquent\Builder; public static function table(Table $table): Table{ return $table ->columns([ Tables\Columns\TextColumn::make('name'), Tables\Columns\TextColumn::make('email'), // ... ]) ->filters([ Tables\Filters\Filter::make('verified') ->query(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at')), // ... ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]);}
请查阅表格构造器文档,了解如何添加表格列、过滤器、Action 等。
授权
关于授权,Filament 会监听所有应用中注册的模型策略。使用如下方法:
viewAny()
用于完全从导航菜单中隐藏资源,阻止用户访问任何页面。create()
用于控制新建记录。update()
用于控制编辑记录。view()
用于控制查看记录。delete()
用于阻止单条记录被删除。deleteAny()
用于防止记录被批量删除。Filament 使用deleteAny()
方法因为通过迭代检查delete()
策略性能不是很好。forceDelete()
用于防止单条软删除记录被强制删除。forceDeleteAny()
用于防止记录被批量强制删除。Filament 之所以使用forceDeleteAny()
因为在多条记录中迭代检查forceDelete()
策略性能不佳。restore()
用于防止单条软删除记录被强制恢复。restoreAny()
用于防止记录被批量强制删除。Filament 之所以使用restoreAny()
因为在多条记录中迭代检查restore()
策略性能不佳。reorder()
用于控制重新排序。
跳过授权
如果你想让某个资源跳过授权,可以将 $shouldSkipAuthorization
属性设置为 true
:
protected static bool $shouldSkipAuthorization = true;
自定义模型标签
每个资源都有一个基于模型名自动生成的"模型标签(model label)"。比如,App\Models\Customer
有一个 Customer
标签。
该标签用在 UI 的多个位置中,你可以使用 $modelLabel
属性对其进行自定义:
protected static ?string $modelLabel = 'cliente';
此外,你也可以使用 getModelLabel()
方法去定义动态标签:
public static function getModelLabel(): string{ return __('filament/resources/customer.label');}
自定义复数模型标签
资源同时有一个基于模型标签自动生成的"复数模型标签(plural model label)"。比如,customer
标签的复数是 customers
。
你可以使用 $pluralModelLabel
属性自定义标签的复数版本:
protected static ?string $pluralModelLabel = 'clientes';
此外,你可以在 getPluralModelLabel()
方法中设置动态复数标签:
public static function getPluralModelLabel(): string{ return __('filament/resources/customer.plural_label');}
模型标签自动大写
默认情况下,对于 UI 的某些部分,Filament 将自动将模型标签中的每个单词大写。例如,在页面标题、导航菜单和面包屑中。
如果你想在资源中禁用该行为,你可以在资源中设置 $hasTitleCaseModelLabel
:
protected static bool $hasTitleCaseModelLabel = false;
资源导航项
Filament 会使用复数标签为资源自动生成导航菜单项。
如果你想自定义导航项标签,你可以使用 $navigationLabel
属性:
protected static ?string $navigationLabel = 'Mis Clientes';
此外,你可以在 getNavigationLabel()
设置为动态导航标签:
public static function getNavigationLabel(): string{ return __('filament/resources/customer.navigation_label');}
设置资源导航图标
$navigationIcon
属性支持任何 Blade 模板组件名。默认情况下,Filament 安装了 Heroicons。不过,你可以创建自定义图标组件或者安装其他图标库。
protected static ?string $navigationIcon = 'heroicon-o-user-group';
此外,你可以在 getNavigationIcon()
方法中设置动态导航图标:
use Illuminate\Contracts\Support\Htmlable; public static function getNavigationIcon(): string | Htmlable | null{ return 'heroicon-o-user-group';}
资源导航项排序
$navigationSort
属性让你可以指定导航项的排列顺序:
protected static ?int $navigationSort = 2;
此外,在 getNavigationSort()
方法中你可以动态设置导航项排序:
public static function getNavigationSort(): ?int{ return 2;}
导航菜单分组
你可以通过 $navigationGroup
属性对导航菜单进行分组:
protected static ?string $navigationGroup = 'Shop';
此外,你也可以使用 getNavigationGroup()
方法设置动态分组标签:
public static function getNavigationGroup(): ?string{ return __('filament/navigation.groups.shop');}
将资源导航项分组到一个项目下
将父级项目的标签传入到 $navigationParentItem
,你可以将导航项作为其他项目的子项目进行分组:
protected static ?string $navigationParentItem = 'Products'; protected static ?string $navigationGroup = 'Shop';
如上所示,如果父级项目有一个导航分组,则必须定义该导航组,这样才能正确识别父级项目。
你也可以调用 $getNavigationParentItem()
方法,动态设置父级项目标签:
public static function getNavigationParentItem(): ?string{ return __('filament/navigation.groups.shop.items.products');}
为资源页生成 URL
Filament 在资源类中使用 getUrl()
静态方法,用来为资源和特定页面生成 URL。一般来说,你需要手动或使用 Laravel 的 route()
帮助函数来构建 URL,而这些方法依赖于对资源的 slug 或路由命名规范的了解。
不带任何参数的 getUrl()
方法,将会为资源列表页生成 URL:
use App\Filament\Resources\CustomerResource; CustomerResource::getUrl(); // /admin/customers
你可以为资源中的指定页面生成 URL。每个页面的名字是资源 getPages()
数组中的数组键名。比如,要为新建页生成 URL:
use App\Filament\Resources\CustomerResource; CustomerResource::getUrl('create'); // /admin/customers/create
getPages()
方法中的一些页面使用了 URL 参数[比如,record
]。要为这些页面生成 URL,并传入记录,你可以使用第二个参数:
use App\Filament\Resources\CustomerResource; CustomerResource::getUrl('edit', ['record' => $customer]); // /admin/customers/edit/1
本例中,$customer
可以是一个 Eloquent 模型对象,或者 ID。
资源模态框生成 URL
如果你只在一个页面上使用简单资源,这将特别有用。
要为资源表中的 Action 生成 URL,应将 tableAction
和 tableActionRecord
作为 URL 参数传递:
use App\Filament\Resources\CustomerResource;use Filament\Tables\Actions\EditAction; CustomerResource::getUrl(parameters: [ 'tableAction' => EditAction::getDefaultName(), 'tableActionRecord' => $customer,]); // /admin/customers?tableAction=edit&tableActionRecord=1
或者如果你想为页面的 Action(如页面头部的 createAcion
) 生成 URL,你可以将其传到 action
参数中:
use App\Filament\Resources\CustomerResource;use Filament\Actions\CreateAction; CustomerResource::getUrl(parameters: [ 'action' => CreateAction::getDefaultName(),]); // /admin/customers?action=create
为其他面板中的资源生成 URL
如果你的应用中有多个面板,getUrl()
将会在当前面板生成一个 URL。你也可以传入面板给 panel
参数,说明该资源应该关联到哪个面板:
use App\Filament\Resources\CustomerResource; CustomerResource::getUrl(panel: 'marketing');
自定义资源 Eloquent 查询
在 Filament 中,每个资源模型的查询都会从 getEloquentQuery()
方法开始。
因此,可以很容易使用你自己的查询约束或影响整个资源的模型范围(scope)查询:
public static function getEloquentQuery(): Builder{ return parent::getEloquentQuery()->where('is_active', true);}
禁用全局范围查询
默认情况下,Filament 会监听所有注册到模型的全局范围查询。不过,如果你想要获取比如软删除记录时,这可能不是理想的方案。
要解决此问题,你可以重写 getEloquentQuery()
方法:
public static function getEloquentQuery(): Builder{ return parent::getEloquentQuery()->withoutGlobalScopes();}
此外,你也可以移除指定的全局范围查询:
public static function getEloquentQuery(): Builder{ return parent::getEloquentQuery()->withoutGlobalScopes([ActiveScope::class]);}
更多关于移除全局范围查询的内容,可以查看 Laravel 文档。
自定义资源 URL
默认情况下,Filament 会基于资源名称生成 URL。你可以在资源上设置 $slug
属性对其进行自定义:
protected static ?string $slug = 'pending-orders';
资源子导航
子导航允许用户在资源中的不同页面之间跳转。通常,所有子导航中的页面与资源中的同一个记录相关联。比如,在 Customer 资源中,你可能有以下页面的子导航:
- 查看客户,一个
ViewRecord
页面提供了客户详细的只读视图。 - 编辑客户,一个
EditRecord
页面允许用户编辑客户详情。 - 编辑用户联系方式,一个允许用户编辑客户联系方式的
EditRecord
页面。学习如何创建多个编辑页。 - 管理地址,一个允许用户管理客户地址的
ManageRelatedRecords
页面。 - 管理支付,一个允许用户管理用户付款的
ManageRelatedRecords
页面。
要在资源的每个"单个记录"页面中添加子导航,你可以添加 getRecordSubNavigation()
方法到资源类中:
use App\Filament\Resources\CustomerResource\Pages;use Filament\Resources\Pages\Page; public static function getRecordSubNavigation(Page $page): array{ return $page->generateNavigationItems([ Pages\ViewCustomer::class, Pages\EditCustomer::class, Pages\EditCustomerContact::class, Pages\ManageCustomerAddresses::class, Pages\ManageCustomerPayments::class, ]);}
子导航中的每一项都可以使用与正常页面相同的导航方法进行自定义。
如果你想要在整个资源和自定义页面之间添加子导航,你可以使用 Cluster,它用于将其分组到一起。
getRecordSubNavigation()
方法旨在构建与资源内特定记录相关的页面之间的导航。
子导航位置
默认情况下,子导航在页面的开始位置进行渲染。你可以在资源的 $subNavigationPosition
属性上设置修改其位置。值可以是SubNavigationPosition::Start
、SubNavigationPosition::End
或 SubNavigationPosition::Top
,用来将子导航渲染成选项卡:
use Filament\Pages\SubNavigationPosition; protected static SubNavigationPosition $subNavigationPosition = SubNavigationPosition::End;
删除资源页
如果你要从你的资源(resource)中删除一个页面,你只需要删除资源 Pages
目录下的对应文件,以及它在 getPages()
方法下的入口。
比如,你有一个资源不想让任何人添加记录。删除 Create
页面文件,然后在 getPages()
中将其移除:
public static function getPages(): array{ return [ 'index' => Pages\ListCustomers::route('/'), 'edit' => Pages\EditCustomer::route('/{record}/edit'), ];}
删除页面不会删除链接到该页面的 Action。所有 Action 将会打开模态框而不是将用户发送到不存在的页面。比如,列表页的 CreateAction
、表格或查看页中的 EditAction
、表格或查看页上的 ViewAction
。如果你想移除这些按钮,你必须同时删除这些 Action。
Still need help? Join our Discord community or open a GitHub discussion