表单构造器
开始
准备 Livewire 组件
实现 HasForms
接口,使用 InteractsWithForms
trait:
<?php namespace App\Http\Livewire; use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms { use Forms\Concerns\InteractsWithForms; public function render(): View { return view('edit-post'); }}
在 Livewire 组件视图中,渲染表单:
<form wire:submit.prevent="submit"> {{ $this->form }} <button type="submit"> Submit </button></form>
最后,将表单字段控件和布局组件添加到 getFormSchema()
方法中:
<?php namespace App\Http\Livewire; use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Post $post; public $title; public $content; public function mount(): void { $this->form->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), // ... ]; } public function submit(): void { // ... } public function render(): View { return view('edit-post'); }}
在浏览器中访问 Livewire 组件,你就能看到 getFormSchema()
方法中的表单组件。
初始化表单
当 Livewire 组件被加载时,你就必须初始化表单。这个过程由 fill()
表单方法实现,通常是在 Livewire 组件的 mount()
方法调用。
要让这些字段加载数据,应该在 Livewire 组件上给他们添加一个相应的属性名,正如普通的 Livewire 组件一般。
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class CreatePost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public $title = ''; public $content = ''; public function mount(): void { $this->form->fill(); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title') ->default('Status Update') ->required(), Forms\Components\MarkdownEditor::make('content'), ]; } public function render(): View { return view('create-post'); }}
你可以使用 afterStateHydrated()
方法自定义表单填充后的操作。
表单数据填充
要填充表单数据,须在表单上调用 fill()
方法,传入一个数组作为参数以供填充:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Post $post; public $title; public $content; public function mount(): void { $this->form->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), ]; } public function render(): View { return view('edit-post'); }}
从表单获取数据
要在数组中获取所有表单数据,需要在表单中调用 getState()
方法。
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class CreatePost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public $title = ''; public $content = ''; public function mount(): void { $this->form->fill(); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), ]; } public function create(): void { Post::create($this->form->getState()); } public function render(): View { return view('create-post'); }}
当 getState()
运行时:
你可以使用
dehydrateStateUsing()
方法,对已经脱水的数据进行转换。
注册模型
你可以给表单注册一个模型。表单构造器可以用这个模型解锁 DX 特性。如:
- 当使用像
exists
和unique
这样的数据库验证规则时,会自动检索数据库名 - 当使用像
Select
、Repeater
、SpatieMediaLibraryFileUpload
或SpatieTagsInput
这样的字段控件时,保存表单时会自动将关联同时附加到模型中。
使用 getFormModel()
将模型实例作为参数到表单中:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Post $post; public $title; public $content; public $tags; public function mount(): void { $this->form->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), Forms\Components\SpatieTagsInput::make('tags'), ]; } protected function getFormModel(): Post { return $this->post; } public function render(): View { return view('edit-post'); }}
此外,使用 model()
方法,你也可以将模型实例作为参数,传入到需要它的表单字段控件之中:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Post $post; public $title; public $content; public $tags; public function mount(): void { $this->form->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), Forms\Components\SpatieTagsInput::make('tags')->model($this->post), ]; } public function render(): View { return view('edit-post'); }}
现在你可以使用关联字段了;
注册模型类
某些情况下,在表单提交之前模型实例不可用。比如,新建 post 的表单:在提交表单之前,post 模型的实例不会被传到表单里。
你可以通过注册模型类代替,取得如前面注册模型的一些特性:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class CreatePost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public $title = ''; public $content = ''; public $categories = []; public function mount(): void { $this->form->fill(); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title') ->required(), Forms\Components\MarkdownEditor::make('content'), Forms\Components\Select::make('categories') ->multiple() ->relationship('categories', 'name'), ]; } protected function getFormModel(): string { return Post::class; } public function render(): View { return view('create-post'); }}
现在你可以使用关联字段了。
关联字段
有一些字段,如 Select
、Repeater
、SpatieMediaLibraryFileUpload
或 SpatieTagsInput
可以与模型关联交互。
比如,Select
可用于添加多条记录到 BelongstoMany
关联模型中。当注册模型到表单或者组件时,这些关联字段也会在调用 getState()
时 自动被保存到关联的透视表中:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Post $post; public $title; public $content; public $categories; public function mount(): void { $this->form->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title') ->required(), Forms\Components\MarkdownEditor::make('content'), Forms\Components\Select::make('categories') ->multiple() ->relationship('categories', 'name'), ]; } protected function getFormModel(): Post { return $this->post; } public function save(): void { $this->post->update( $this->form->getState(), ); } public function render(): View { return view('edit-post'); }}
手动保存关联字段
某些情况下,在表单提交之前模型实例不可用。比如,在新建 post 的表单中,提交表单前 post 的实例不会被传到表单 form 里。这种情况下,你需要传模型类代替,不过,关联字段就需要随后手动进行保存了。
这种情况下,你可以在实例创建后在表单上调用 model()
和 saveRelationships()
方法:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Illuminate\Database\Eloquent\Model;use Livewire\Component; class CreatePost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public $title = ''; public $content = ''; public $tags = []; public function mount(): void { $this->form->fill(); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), Forms\Components\SpatieTagsInput::make('tags'), ]; } protected function getFormModel(): string { return Post::class; } public function create(): void { $post = Post::create($this->form->getState()); $this->form->model($post)->saveRelationships(); } public function render(): View { return view('create-post'); }}
字段隐藏时保存关联数据
默认情况下,关联只会在字段可见的情况下保存。比如,假设你有一个 Repeater
字段,只在特定条件下可见;这种情况下,当组件隐藏时,关联不会被保存。
如果你在字段隐藏时仍然想要保存关联数据,这样可能会带来意料之外的行为。要使关联数据强制保存,你可以在表单组件中调用 saveRelationshipsWhenHidden()
方法:
use Filament\Forms\Components\SpatieMediaLibraryFileUpload; SpatieMediaLibraryFileUpload::make('attachments') ->visible(fn (Closure $get): bool => $get('has_attachments')) ->saveRelationshipsWhenHidden();
使用多重表单
默认情况下,每个 Livewire 组件中 InteractsWithForm
trait 只处理一个表单。要实现多重表单,你可以重写 getForms()
方法让他返回多个表单,并让每个表单都有一个唯一的表单名:
<?php namespace App\Http\Livewire; use App\Models\Author;use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Author $author; public Post $post; public $title; public $content; public $name; public $email; public function mount(): void { $this->postForm->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); $this->authorForm->fill([ 'name' => $this->author->name, 'email' => $this->author->email, ]); } protected function getPostFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), ]; } protected function getAuthorFormSchema(): array { return [ Forms\Components\TextInput::make('name')->required(), Forms\Components\TextInput::make('email')->email()->required(), ]; } public function savePost(): void { $this->post->update( $this->postForm->getState(), ); } public function saveAuthor(): void { $this->author->update( $this->authorForm->getState(), ); } protected function getForms(): array { return [ 'postForm' => $this->makeForm() ->schema($this->getPostFormSchema()) ->model($this->post), 'authorForm' => $this->makeForm() ->schema($this->getAuthorFormSchema()) ->model($this->author), ]; } public function render(): View { return view('edit-post'); }}
将表单数据纳入数组属性
你可以在 Livewire 组件上将所有表单数据归入到一个类型为数组的属性中。这样你就可以不必为每个字段定义新属性:
<?php namespace App\Http\Livewire; use App\Models\Post;use Filament\Forms;use Illuminate\Contracts\View\View;use Livewire\Component; class EditPost extends Component implements Forms\Contracts\HasForms{ use Forms\Concerns\InteractsWithForms; public Post $post; public $data; public function mount(): void { $this->form->fill([ 'title' => $this->post->title, 'content' => $this->post->content, ]); } protected function getFormSchema(): array { return [ Forms\Components\TextInput::make('title')->required(), Forms\Components\MarkdownEditor::make('content'), Forms\Components\SpatieTagsInput::make('tags'), ]; } protected function getFormModel(): Post { return $this->post; } protected function getFormStatePath(): string { return 'data'; } public function render(): View { return view('edit-post'); }}
此例中,所有表单数据都会被存到 $data
数组中。
Still need help? Join our Discord community or open a GitHub discussion