资源是用于为您的 Eloquent 模型构建 CRUD 接口的静态类。它们描述了管理员应如何使用表格和表单与您的应用程序中的数据进行交互。
为 App\Models\Customer 模型创建一个资源:
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 目录中的类用于定义您资源的 表单 和 信息列表 的内容。Tables 目录中的类用于构建您资源的表。
您是否创建了资源,但它未在导航菜单中显示?如果您有 模型策略,请确保您从 viewAny() 方法中返回 true。
有时, 你的模型足够简单, 以至于你只想在单个页面上管理记录, 使用模态框来创建, 编辑和删除记录. 要生成一个带有模态框的简单资源:
php artisan make:filament-resource Customer --simple您的资源将拥有一个“管理”页面,这是一个添加了模态框的列表页面。
此外,你的简单资源将没有 getRelations() 方法,因为 关系管理器 只在编辑和查看页面上显示,而这些页面在简单资源中是不存在的。其他一切都相同。
如果您想节省时间,Filament 可以根据您的模型数据库列,使用 --generate 自动为您生成 表单 和 表格:
php artisan make:filament-resource Customer --generate默认情况下,您将无法与应用中已删除的记录进行交互。如果您想为您的资源添加恢复、强制删除和筛选被删除记录的功能,请在生成资源时使用 --soft-deletes 标志:
php artisan make:filament-resource Customer --soft-deletes您可以了解更多关于软删除的信息 在此。
默认情况下,您的资源只会生成列表、创建和编辑页面。如果您还想要一个查看页面,请使用 --view 标志:
php artisan make:filament-resource Customer --view默认情况下,Filament 会假定您的模型位于 App\Models 目录。您可以使用 --model-namespace 标志为模型传递不同的命名空间:
php artisan make:filament-resource Customer --model-namespace=Custom\\Path\\Models在此示例中,模型应位于 Custom\Path\Models\Customer。请注意命令中必需的双反斜杠 \\。
现在当 生成资源, Filament 将能够定位模型并读取数据库结构。
如果您想在搭建资源时节省时间,Filament 还可以同时为新资源生成模型、迁移和工厂,使用 --model、--migration 和 --factory 标记的任意组合:
php artisan make:filament-resource Customer --model --migration --factory可以为您的资源设置一个$recordTitleAttribute,它是您模型上可用于将该资源与其他资源区分开来的列的名称。
例如,这可以是博客文章的 标题 或客户的 姓名:
protected static ?string $recordTitleAttribute = 'name';这是 全局搜索 等功能正常工作所必需的。
您可以指定一个 Eloquent 访问器 的名称如果仅一列不足以识别一条记录。
资源类包含一个 form() 方法,该方法用于在 创建 和 编辑 页面上构建表单。
默认情况下,Filament 会为你创建一个表单 schema 文件,该文件会在 form() 方法中被引用。这样做是为了让你的资源类保持整洁有序,否则它可能会变得相当庞大:
use App\Filament\Resources\Customers\Schemas\CustomerForm;
use Filament\Schemas\Schema;
public static function form(Schema $schema): Schema
{
return CustomerForm::configure($schema);
}在 CustomerForm 类中,您可以定义表单的字段和布局:
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 构建表单。
如果您倾向于直接在资源类中定义表单,您可以这样做并完全删除表单模式类:
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() 方法允许你基于当前页面或操作动态隐藏字段。
在此示例中,我们隐藏 password 字段在 edit 页面:
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\Operation;
TextInput::make('password')
->password()
->required()
->hiddenOn(Operation::Edit),另外,我们有一个 visibleOn() 快捷方法,用于仅在一个页面或动作上显示一个字段:
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\Operation;
TextInput::make('password')
->password()
->required()
->visibleOn(Operation::Create),资源类包含一个 table() 方法,用于在 列表页 上构建表格。
默认情况下,Filament 会为你创建一个表格文件,该文件在 table() 方法中被引用。这样做是为了保持你的资源类整洁有序,否则它可能会变得相当庞大:
use App\Filament\Resources\Customers\Tables\CustomersTable;
use Filament\Tables\Table;
public static function table(Table $table): Table
{
return CustomersTable::configure($table);
}在 CustomersTable 类中,您可以定义表的列、筛选器和操作:
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(),
]),
]);
}查阅表格文档,了解如何添加表格列、筛选器、操作等等。
如果您更倾向于直接在资源类中定义表,您可以这样做并完全删除表类:
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 属性自定义它:
protected static ?string $modelLabel = 'cliente';或者,您可以使用 getModelLabel() 来定义一个动态标签:
public static function getModelLabel(): string
{
return __('filament/resources/customer.label');
}资源也有一个“复数模型标签”,其会自动从模型标签生成。例如,一个 customer 标签将被复数化为 customers。
您可以自定义标签的复数版本,使用 $pluralModelLabel 属性:
protected static ?string $pluralModelLabel = 'clientes';或者,您可以在 getPluralModelLabel() 方法中设置一个动态的复数标签:
public static function getPluralModelLabel(): string
{
return __('filament/resources/customer.plural_label');
}默认情况下,Filament 将自动将模型标签中的每个单词大写,用于 UI 的某些部分。例如,在页面标题、导航菜单和面包屑中。
如果您想为某个资源禁用此行为,可以在该资源中设置 $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 组件的名称。默认情况下,Heroicons 已安装。但是,您也可以根据需要创建自己的自定义图标组件或安装其他库。
use BackedEnum;
protected static string | BackedEnum | null $navigationIcon = 'heroicon-o-user-group';或者,您可以在 getNavigationIcon() 方法中设置一个动态导航图标:
use BackedEnum;
use Illuminate\Contracts\Support\Htmlable;
public static function getNavigationIcon(): string | BackedEnum | Htmlable | null
{
return 'heroicon-o-user-group';
}该 $navigationSort 属性允许您指定导航项的列出顺序:
protected static ?int $navigationSort = 2;另外,您可以在 getNavigationSort() 方法中设置动态导航项顺序:
public static function getNavigationSort(): ?int
{
return 2;
}您可以将导航项分组 通过指定一个 $navigationGroup 属性:
use UnitEnum;
protected static string | UnitEnum | null $navigationGroup = 'Shop';或者,您可以使用 getNavigationGroup() 方法来设置一个动态组标签:
public static function getNavigationGroup(): ?string
{
return __('filament/navigation.groups.shop');
}你可以将导航项分组为其他项的子项,通过将父项的标签作为 $navigationParentItem 传递:
use UnitEnum;
protected static ?string $navigationParentItem = 'Products';
protected static string | UnitEnum | null $navigationGroup = 'Shop';如上所示,如果父项拥有导航组,则该导航组也必须被定义,以便可以识别出正确的父项。
您还可以使用 getNavigationParentItem() 方法来设置动态的父项标签:
public static function getNavigationParentItem(): ?string
{
return __('filament/navigation.groups.shop.items.products');
}Filament 在资源类上提供了一个 getUrl() 静态方法, 用于生成指向资源及其内部特定页面的 URL. 传统上, 您需要手动构造 URL 或使用 Laravel 的 route() 助手函数, 但这些方法依赖于对资源 slug 或路由命名约定的了解.
getUrl() 方法,不带任何参数,将生成一个 URL 到资源的 列表页:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl(); // /admin/customers您也可以生成指向资源内特定页面的 URL.
每个页面的名称是资源中 getPages() 数组中的键.
例如,要生成指向 创建页面 的 URL:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl('create'); // /admin/customers/creategetPages() 方法中的某些页面使用像 record 这样的 URL 参数。要生成指向这些页面的 URL 并传入一个记录,您应该使用第二个参数:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl('edit', ['record' => $customer]); // /admin/customers/edit/1在此示例中,$customer 可以是一个 Eloquent 模型对象,或者一个 ID。
这在您使用简单资源且仅包含一个页面时会特别有用。
要在资源的表中为某个动作生成 URL,你应该将 tableAction 和 tableActionRecord 作为 URL 参数传递:
use App\Filament\Resources\Customers\CustomerResource;
use Filament\Actions\EditAction;
CustomerResource::getUrl(parameters: [
'tableAction' => EditAction::getDefaultName(),
'tableActionRecord' => $customer,
]); // /admin/customers?tableAction=edit&tableActionRecord=1或者,如果你想为页面上的一个操作生成一个 URL,例如标题中的 CreateAction,你可以将其传入 action 参数:
use App\Filament\Resources\Customers\CustomerResource;
use Filament\Actions\CreateAction;
CustomerResource::getUrl(parameters: [
'action' => CreateAction::getDefaultName(),
]); // /admin/customers?action=create如果您的应用中有多个面板,getUrl() 将在当前面板内生成一个 URL。您还可以指明资源与哪个面板相关联,通过将面板 ID 传递给 panel 参数:
use App\Filament\Resources\Customers\CustomerResource;
CustomerResource::getUrl(panel: 'marketing');在 Filament 中,每个针对你的资源模型的查询都将从 getEloquentQuery() 方法开始。
正因如此,可以非常轻松地应用您自己的查询约束或 模型作用域 来影响整个资源:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->where('is_active', true);
}默认情况下,Filament 将遵循注册到你的模型上的所有全局作用域。然而,如果你想访问,例如,软删除的记录,这可能不理想。
为了克服这个问题,您可以重写 Filament 使用的 getEloquentQuery() 方法:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes();
}或者,您可以移除特定的全局作用域:
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes([ActiveScope::class]);
}关于移除全局作用域的更多信息,请参阅 Laravel 文档.
默认情况下,Filament 会基于资源的名称生成一个 URL。你可以在资源上设置 $slug 属性来对其进行自定义:
protected static ?string $slug = 'pending-orders';子导航允许用户在资源内不同页面之间进行导航。通常,子导航中的所有页面都与资源中的同一条记录相关。例如,在“客户”资源中,您可能有一个包含以下页面的子导航:
ViewRecord 页面,提供客户详情的只读视图。编辑客户, 一个编辑记录 页面,允许用户编辑客户的详细信息。EditRecord 页面 允许用户编辑客户的联系方式。 你可以 了解如何创建多个编辑页面。ManageRelatedRecords 页面 允许用户管理客户地址。ManageRelatedRecords 页面 允许用户管理客户的支付。要在资源中为每个“单个记录”页面添加子导航,您可以将 getRecordSubNavigation() 方法添加到资源类:
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,
]);
}子导航中的每个项目都可以使用与普通页面相同的导航方法。
子导航默认在页面开头渲染。你可以通过在资源上设置 $subNavigationPosition 属性更改资源中所有页面的位置。该值可以是 SubNavigationPosition::Start,SubNavigationPosition::End,或 SubNavigationPosition::Top 以将子导航渲染为选项卡:
use Filament\Pages\Enums\SubNavigationPosition;
protected static ?SubNavigationPosition $subNavigationPosition = SubNavigationPosition::End;如果你想从你的资源中删除一个页面,你只需从资源的Pages目录中删除该页面文件,以及在getPages()方法中删除其条目。
例如,您可能有一个资源,其记录可能不是由任何人创建的。删除 Create 页面文件,然后从 getPages() 中将其移除:
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:
protected static bool $shouldSkipAuthorization = true;Filament 将向 JavaScript 暴露所有模型属性,除非它们在你的模型中被设置为 $hidden。这是 Livewire 的模型绑定行为。我们保留此功能,以便在表单字段初次加载后,方便其动态添加和移除,同时保留它们可能需要的数据。
虽然属性可能在 JavaScript 中可见,但只有那些具有表单字段的才能实际被用户编辑。这与批量赋值无关。
To remove certain attributes from JavaScript on the Edit and View pages, you may override the mutateFormDataBeforeFill() method:
protected function mutateFormDataBeforeFill(array $data): array
{
unset($data['is_admin']);
return $data;
}在此示例中,我们移除了 JavaScript 中的 is_admin 属性,因为它未被表单使用.