验证
Livewire 旨在让验证用户输入并向其提供反馈尽可能轻松。它建立在 Laravel 验证功能之上,既能复用你已有的知识,又提供了实时验证等强大附加能力。
下面的 CreatePost 组件示例演示了 Livewire 中最基本的验证流程:
<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
public $title = '';
public $content = '';
public function save()
{
$validated = $this->validate([ // [tl! highlight:3]
'title' => 'required|min:3',
'content' => 'required|min:3',
]);
Post::create($validated);
return redirect()->to('/posts');
}
public function render()
{
return view('livewire.create-post');
}
}<form wire:submit="save">
<input type="text" wire:model="title">
<div>@error('title') {{ $message }} @enderror</div>
<textarea wire:model="content"></textarea>
<div>@error('content') {{ $message }} @enderror</div>
<button type="submit">Save</button>
</form>如你所见,Livewire 提供了可调用的 validate() 方法来验证组件属性。它返回已验证的数据集,随后你可以安全地插入数据库。
在前端,你可以使用 Laravel 现有的 Blade 指令向用户显示验证消息。
更多信息请参阅 Laravel 关于在 Blade 中渲染验证错误的文档。
Validate 属性
若你更希望将组件的验证规则与属性放在一起,可以使用 Livewire 的 #[Validate] 属性。
通过 #[Validate] 将验证规则与属性关联后,Livewire 会在每次更新前自动运行这些属性的验证规则。不过,在将数据持久化到数据库之前,你仍应运行 $this->validate(),以便尚未更新的属性也会被验证。
use Livewire\Attributes\Validate;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
#[Validate('required|min:3')] // [tl! highlight]
public $title = '';
#[Validate('required|min:3')] // [tl! highlight]
public $content = '';
public function save()
{
$this->validate();
Post::create([
'title' => $this->title,
'content' => $this->content,
]);
return redirect()->to('/posts');
}
// ...
}INFO
Validate 属性不支持 Rule 对象
PHP Attributes 仅限于纯字符串和数组等特定语法。若你想使用运行时语法(例如 Laravel 的 Rule 对象 Rule::exists(...)),应改为在组件中定义 rules() 方法。
详见 在 Livewire 中使用 Laravel Rule 对象 的文档。
若你希望对属性何时验证有更多控制,可以向 #[Validate] 属性传入 onUpdate: false 参数。这将禁用任何自动验证,并假定你要用 $this->validate() 方法手动验证属性:
use Livewire\Attributes\Validate;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
#[Validate('required|min:3', onUpdate: false)]
public $title = '';
#[Validate('required|min:3', onUpdate: false)]
public $content = '';
public function save()
{
$validated = $this->validate();
Post::create($validated);
return redirect()->to('/posts');
}
// ...
}自定义属性名称
若要自定义注入到验证消息中的属性名称,可使用 as: 参数:
use Livewire\Attributes\Validate;
#[Validate('required', as: 'date of birth')]
public $dob;上例验证失败时,Laravel 会在验证消息中用「date of birth」而不是「dob」作为字段名。生成的消息将是「The date of birth field is required」,而不是「The dob field is required」。
自定义验证消息
要绕过 Laravel 的验证消息并换成你自己的,可在 #[Validate] 属性中使用 message: 参数:
use Livewire\Attributes\Validate;
#[Validate('required', message: 'Please provide a post title')]
public $title;现在,该属性验证失败时,消息将是「Please provide a post title」,而不是「The title field is required」。
若要为不同规则添加不同消息,只需提供多个 #[Validate] 属性:
#[Validate('required', message: 'Please provide a post title')]
#[Validate('min:3', message: 'This title is too short')]
public $title;退出本地化
默认情况下,Livewire 规则消息和属性会使用 Laravel 的翻译助手 trans() 进行本地化。
可通过向 #[Validate] 属性传入 translate: false 参数来退出本地化:
#[Validate('required', message: 'Please provide a post title', translate: false)]
public $title;自定义键
用 #[Validate] 属性直接给属性应用验证规则时,Livewire 假定验证键就是属性名本身。但有时你可能希望自定义验证键。
例如,你可能希望为数组属性及其子项分别提供验证规则。此时,不要把验证规则作为 #[Validate] 的第一个参数,而是传入键值对数组:
#[Validate([
'todos' => 'required',
'todos.*' => [
'required',
'min:3',
new Uppercase,
],
])]
public $todos = [];现在,当用户更新 $todos,或调用 validate() 方法时,这两条验证规则都会被应用。
表单对象
随着更多属性和验证规则加入 Livewire 组件,组件可能显得过于拥挤。为缓解这一问题并提供便于代码复用的抽象,你可以使用 Livewire 的 表单对象(Form Objects) 来存放属性和验证规则。
下面仍是 CreatePost 示例,但属性和规则已提取到名为 PostForm 的专用表单对象中:
<?php
namespace App\Livewire\Forms;
use Livewire\Attributes\Validate;
use Livewire\Form;
class PostForm extends Form
{
#[Validate('required|min:3')]
public $title = '';
#[Validate('required|min:3')]
public $content = '';
}上面的 PostForm 现在可定义为 CreatePost 组件上的属性:
<?php
namespace App\Livewire;
use App\Livewire\Forms\PostForm;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
public PostForm $form;
public function save()
{
Post::create(
$this->form->all()
);
return redirect()->to('/posts');
}
// ...
}如你所见,不必逐一列出每个属性,可以用表单对象上的 ->all() 方法获取全部属性值。
另外,在模板中引用属性名时,必须在每一处前面加上 form.:
<form wire:submit="save">
<input type="text" wire:model="form.title">
<div>@error('form.title') {{ $message }} @enderror</div>
<textarea wire:model="form.content"></textarea>
<div>@error('form.content') {{ $message }} @enderror</div>
<button type="submit">Save</button>
</form>使用表单对象时,每次属性更新都会运行 #[Validate] 属性验证。但若通过在属性上指定 onUpdate: false 禁用该行为,则可用 $this->form->validate() 手动运行表单对象的验证:
public function save()
{
Post::create(
$this->form->validate()
);
return redirect()->to('/posts');
}对于大多数较大的数据集,表单对象是有用的抽象,还有多种附加功能使其更加强大。更多信息请参阅完整的表单对象文档。
实时验证
实时验证指在用户填写表单时就验证输入,而不是等到提交表单。
通过在 Livewire 属性上直接使用 #[Validate],每当发送网络请求以在服务器上更新属性值时,都会应用所提供的验证规则。
这意味着要为特定输入提供实时验证体验,无需额外的后端工作。唯一需要的是使用 wire:model.live 或 wire:model.live.blur,指示 Livewire 在填写字段时触发网络请求。
下例中,文本输入加了 wire:model.live.blur。现在,用户在字段中输入后 Tab 或点击离开该字段时,会用更新后的值触发网络请求并运行验证规则:
<form wire:submit="save">
<input type="text" wire:model.live.blur="title">
<!-- -->
</form>若你用 rules() 方法而不是 #[Validate] 属性来声明属性的验证规则,仍可加上无参数的 #[Validate] 属性以保留实时验证行为:
use Livewire\Attributes\Validate;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
#[Validate] // [tl! highlight]
public $title = '';
public $content = '';
protected function rules()
{
return [
'title' => 'required|min:5',
'content' => 'required|min:5',
];
}
public function save()
{
$validated = $this->validate();
Post::create($validated);
return redirect()->to('/posts');
}现在,上例中即使 #[Validate] 为空,它也会告诉 Livewire:每次属性更新时都运行 rules() 提供的字段验证。
自定义错误消息
开箱即用,若 $title 属性附带了 required 规则,Laravel 会提供合理的验证消息,例如「The title field is required.」。
不过,你可能需要自定义这些错误消息的措辞,以更好地适配你的应用和用户。
自定义属性名称
有时被验证的属性名不适合展示给用户。例如,应用中有名为 dob、代表「Date of birth」的数据库字段,你希望向用户显示「The date of birth field is required」,而不是「The dob field is required」。
Livewire 允许你用 as: 参数为属性指定替代名称:
use Livewire\Attributes\Validate;
#[Validate('required', as: 'date of birth')]
public $dob = '';现在,若 required 验证规则失败,错误消息会显示「The date of birth field is required.」,而不是「The dob field is required.」。
自定义消息
若自定义属性名还不够,可用 message: 参数自定义整条验证消息:
use Livewire\Attributes\Validate;
#[Validate('required', message: 'Please fill out your date of birth.')]
public $dob = '';若有多条规则需要自定义消息,建议为每一条使用完全独立的 #[Validate] 属性,如下所示:
use Livewire\Attributes\Validate;
#[Validate('required', message: 'Please enter a title.')]
#[Validate('min:5', message: 'Your title is too short.')]
public $title = '';若改用 #[Validate] 属性的数组语法,可以这样指定自定义属性和消息:
use Livewire\Attributes\Validate;
#[Validate([
'titles' => 'required',
'titles.*' => 'required|min:5',
], message: [
'required' => 'The :attribute is missing.',
'titles.required' => 'The :attribute are missing.',
'min' => 'The :attribute is too short.',
], attribute: [
'titles.*' => 'title',
])]
public $titles = [];定义 rules() 方法
作为 Livewire #[Validate] 属性的替代方案,你可以在组件中定义名为 rules() 的方法,并返回字段列表及对应的验证规则。若你要使用 PHP Attributes 不支持的运行时语法(例如 Laravel 的 Rule::password() 规则对象),这会很有帮助。
这些规则会在你在组件内运行 $this->validate() 时应用。你还可以定义 messages() 和 validationAttributes() 函数。
示例如下:
use Livewire\Component;
use App\Models\Post;
use Illuminate\Validation\Rule;
class CreatePost extends Component
{
public $title = '';
public $content = '';
protected function rules() // [tl! highlight:6]
{
return [
'title' => Rule::exists('posts', 'title'),
'content' => 'required|min:3',
];
}
protected function messages() // [tl! highlight:6]
{
return [
'content.required' => 'The :attribute are missing.',
'content.min' => 'The :attribute is too short.',
];
}
protected function validationAttributes() // [tl! highlight:6]
{
return [
'content' => 'description',
];
}
public function save()
{
$this->validate();
Post::create([
'title' => $this->title,
'content' => $this->content,
]);
return redirect()->to('/posts');
}
// ...
}WARNING
rules() 方法不会在数据更新时验证
通过 rules() 方法定义规则时,Livewire 仅在你运行 $this->validate() 时用这些验证规则验证属性。这与标准 #[Validate] 属性不同——后者会在每次通过 wire:model 等方式更新字段时应用。若要在属性每次更新时都应用这些验证规则,仍可使用无额外参数的 #[Validate]。
WARNING
不要与 Livewire 机制冲突
使用 Livewire 验证工具时,组件不应有名为 rules、messages、validationAttributes 或 validationCustomValues 的属性或方法,除非你在自定义验证过程。否则会与 Livewire 的机制冲突。
使用 Laravel Rule 对象
Laravel Rule 对象是为表单添加高级验证行为的极为强大的方式。
下面示例将 Rule 对象与 Livewire 的 rules() 方法结合,实现更复杂的验证:
<?php
namespace App\Livewire;
use Illuminate\Validation\Rule;
use App\Models\Post;
use Livewire\Form;
class UpdatePost extends Form
{
public ?Post $post;
public $title = '';
public $content = '';
protected function rules()
{
return [
'title' => [
'required',
Rule::unique('posts')->ignore($this->post), // [tl! highlight]
],
'content' => 'required|min:5',
];
}
public function mount()
{
$this->title = $this->post->title;
$this->content = $this->post->content;
}
public function update()
{
$this->validate(); // [tl! highlight]
$this->post->update($this->all());
$this->reset();
}
// ...
}手动控制验证错误
Livewire 的验证工具应能处理最常见的验证场景;但有时你可能希望完全控制组件中的验证消息。
以下是操作 Livewire 组件中验证错误的全部可用方法:
| 方法 | 说明 |
|---|---|
$this->addError([key], [message]) | 手动向错误包添加验证消息 |
$this->resetValidation([?key]) | 重置所提供键的验证错误;未提供键则重置全部错误 |
$this->getErrorBag() | 获取 Livewire 组件内部使用的底层 Laravel 错误包 |
INFO
在表单对象中使用 $this->addError()
在表单对象内用 $this->addError 手动添加错误时,键会自动加上父组件中表单所赋属性名的前缀。例如,若在组件中把表单赋给名为 $data 的属性,键会变成 data.key。
访问验证器实例
有时你可能希望访问 Livewire 在 validate() 方法内部使用的 Validator 实例。可通过 withValidator 方法实现。你提供的闭包会收到已完整构建的验证器作为参数,从而能在验证规则真正求值之前调用其任意方法。
下面示例拦截 Livewire 的内部验证器,以手动检查条件并添加额外验证消息:
use Livewire\Attributes\Validate;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
#[Validate('required|min:3')]
public $title = '';
#[Validate('required|min:3')]
public $content = '';
public function boot()
{
$this->withValidator(function ($validator) {
$validator->after(function ($validator) {
if (str($this->title)->startsWith('"')) {
$validator->errors()->add('title', 'Titles cannot start with quotations');
}
});
});
}
public function save()
{
Post::create($this->all());
return redirect()->to('/posts');
}
// ...
}使用自定义验证器
若你希望在 Livewire 中使用自己的验证系统,完全没问题。Livewire 会捕获组件内抛出的任何 ValidationException 异常,并把错误提供给视图,效果与使用 Livewire 自带的 validate() 方法一样。
下面是 CreatePost 组件示例,但未使用 Livewire 的验证功能,而是创建并应用完全自定义的验证器到组件属性:
use Illuminate\Support\Facades\Validator;
use Livewire\Component;
use App\Models\Post;
class CreatePost extends Component
{
public $title = '';
public $content = '';
public function save()
{
$validated = Validator::make(
// Data to validate...
['title' => $this->title, 'content' => $this->content],
// Validation rules to apply...
['title' => 'required|min:3', 'content' => 'required|min:3'],
// Custom validation messages...
['required' => 'The :attribute field is required'],
)->validate();
Post::create($validated);
return redirect()->to('/posts');
}
// ...
}测试验证
Livewire 为验证场景提供了实用的测试工具,例如 assertHasErrors() 方法。
下面是一个基本测试用例,确保在未为 title 属性设置输入时会抛出验证错误:
<?php
namespace Tests\Feature\Livewire;
use App\Livewire\CreatePost;
use Livewire\Livewire;
use Tests\TestCase;
class CreatePostTest extends TestCase
{
public function test_cant_create_post_without_title()
{
Livewire::test(CreatePost::class)
->set('content', 'Sample content...')
->call('save')
->assertHasErrors('title');
}
}除了测试错误是否存在,assertHasErrors 还允许你通过将要断言的规则作为方法的第二个参数,将断言收窄到特定规则:
public function test_cant_create_post_with_title_shorter_than_3_characters()
{
Livewire::test(CreatePost::class)
->set('title', 'Sa')
->set('content', 'Sample content...')
->call('save')
->assertHasErrors(['title' => ['min:3']]);
}你也可以同时断言多个属性是否存在验证错误:
public function test_cant_create_post_without_title_and_content()
{
Livewire::test(CreatePost::class)
->call('save')
->assertHasErrors(['title', 'content']);
}关于 Livewire 提供的其他测试工具,请参阅测试文档。
在 JavaScript 中访问错误
Livewire 提供 $errors 魔术属性,以便在客户端访问验证错误:
<form wire:submit="save">
<input type="email" wire:model="email">
<div wire:show="$errors.has('email')">
<span wire:text="$errors.first('email')"></span>
</div>
<button type="submit">Save</button>
</form>可用方法
$errors.has('field')- 检查字段是否有错误$errors.missing('field')- 检查字段是否没有错误$errors.first('field')- 获取字段的第一条错误消息$errors.get('field')- 获取字段的全部错误消息$errors.all()- 获取所有字段的全部错误$errors.clear()- 清除全部错误$errors.clear('field')- 清除特定字段的错误
使用 Alpine.js 时,通过 $wire.$errors 访问 $errors。
已弃用的 [#Rule] 属性
Livewire v3 首次发布时,验证属性使用的是「Rule」而非「Validate」(即 #[Rule])。
由于与 Laravel rule 对象存在命名冲突,现已改为 #[Validate]。二者在 Livewire v3 中均受支持,但建议将所有 #[Rule] 替换为 #[Validate] 以保持最新。
另见
- Forms — 用实时反馈验证表单输入
- Properties — 在持久化前验证属性值
- Validate Attribute — 使用 #[Validate] 进行属性验证
- Actions — 在操作方法中验证数据