单例资源
概述
资源并非在 Filament 面板中操作 Eloquent 记录的唯一方式。尽管资源能满足许多需求,但资源的「index」(根)页面包含一张列出该资源记录的表格。
有时并不需要列出资源记录的表格,用户只与单条记录交互。若用户访问页面时记录尚不存在,则在首次提交表单保存时创建;若记录已存在,则在页面首次加载时填入表单,并在提交时更新。
例如,CMS 可能有 Page Eloquent 模型与 PageResource,但你也可能想在 PageResource 之外创建单例页面,用于编辑网站「首页」。这样用户可直接编辑首页,而无需进入 PageResource 并在表格中查找首页记录。
其他例子包括「设置」页,或当前登录用户的「个人资料」页。不过对于这些场景,我们建议使用 Spatie Settings 插件 以及 Filament 的 个人资料 功能,实现所需代码更少。


创建单例资源
首先,创建 自定义页面:
bash
php artisan make:filament-page ManageHomepage该命令会创建两个文件:资源目录下 /Filament/Pages 中的页面类,以及资源视图目录下 /filament/pages 中的 Blade 视图。
页面类应包含以下元素:
- `$data` 属性,用于保存表单当前状态。
- `mount()` 方法,从数据库加载当前记录并用其数据填充表单。若记录不存在,会向表单的 `fill()` 传入 `null`,从而为字段赋默认值。
- `form()` 方法,定义表单 schema。表单字段写在 `components()` 中。应用 `record()` 指定从表单加载关联数据的记录,并用 `statePath()` 指定存放表单状态的属性名(`$data`)。
- `save()` 方法,将表单数据保存到数据库。`getState()` 会运行校验并返回有效数据。该方法应检查记录是否已存在,不存在则新建。可用模型的 `wasRecentlyCreated` 判断是否刚创建,若是则还应保存关联。向用户发送通知以确认已保存。
- `getRecord()` 方法虽非必须,但建议提供。它返回表单正在编辑的 Eloquent 记录,可在其他方法中复用以避免重复代码。
php
namespace App\Filament\Pages;
use App\Models\WebsitePage;
use Filament\Actions\Action;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Schemas\Components\Actions;
use Filament\Schemas\Components\Form;
use Filament\Schemas\Schema;
/**
* @property-read Schema $form
*/
class ManageHomepage extends Page
{
protected string $view = 'filament.pages.manage-homepage';
/**
* @var array<string, mixed> | null
*/
public ?array $data = [];
public function mount(): void
{
$this->form->fill($this->getRecord()?->attributesToArray());
}
public function form(Schema $schema): Schema
{
return $schema
->components([
Form::make([
TextInput::make('title')
->required()
->maxLength(255),
RichEditor::make('content'),
// ...
])
->livewireSubmitHandler('save')
->footer([
Actions::make([
Action::make('save')
->submit('save')
->keyBindings(['mod+s']),
]),
]),
])
->record($this->getRecord())
->statePath('data');
}
public function save(): void
{
$data = $this->form->getState();
$record = $this->getRecord();
if (! $record) {
$record = new WebsitePage();
$record->is_homepage = true;
}
$record->fill($data);
$record->save();
if ($record->wasRecentlyCreated) {
$this->form->record($record)->saveRelationships();
}
Notification::make()
->success()
->title('Saved')
->send();
}
public function getRecord(): ?WebsitePage
{
return WebsitePage::query()
->where('is_homepage', true)
->first();
}
}页面的 Blade 视图应渲染表单:
blade
<x-filament::page>
{{ $this->form }}
</x-filament::page>