Skip to content

验证

Livewire 旨在使验证用户输入并向他们提供反馈尽可能愉快。通过构建在 Laravel 的验证功能之上,Livewire 利用你现有的知识,同时还为你提供强大的附加功能,例如实时验证。

以下是一个 CreatePost 组件示例,演示了 Livewire 中最基本的验证工作流程:

php
<?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');
    }
}
blade
<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(),以便还验证尚未更新的属性。

php
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');
    }

    // ...
}

Validate 属性不支持 Rule 对象

PHP 属性仅限于某些语法,如纯字符串和数组。如果你发现自己想要使用运行时语法,如 Laravel 的 Rule 对象(Rule::exists(...)),你应该在组件中定义 rules() 方法

使用 Laravel Rule 对象与 Livewire 的文档中了解更多信息。

如果你希望更多地控制何时验证属性,可以向 #[Validate] 属性传递 onUpdate: false 参数。这将禁用任何自动验证,而是假设你想要使用 $this->validate() 方法手动验证属性:

php
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');
    }

    // ...
}

Custom attribute name

如果你希望自定义注入到验证消息中的属性名称,可以使用 as: 参数:

php
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: 参数:

php
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] 属性:

php
#[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 参数来选择退出本地化:

php
#[Validate('required', message: 'Please provide a post title', translate: false)]
public $title;

自定义键

当使用 #[Validate] 属性直接将验证规则应用于属性时,Livewire 假设验证键应该是属性本身的名称。但是,有时你可能希望自定义验证键。

例如,你可能希望为数组属性及其子项提供单独的验证规则。在这种情况下,你可以传递键值对数组,而不是将验证规则作为 #[Validate] 属性的第一个参数:

php
#[Validate([
    'todos' => 'required',
    'todos.*' => [
        'required',
        'min:3',
        new Uppercase,
    ],
])]
public $todos = [];

现在,当用户更新 $todos 或调用 validate() 方法时,这两个验证规则都将被应用。

Form 对象

随着更多属性和验证规则被添加到 Livewire 组件中,它可能会开始感觉太拥挤。为了减轻这种痛苦并为代码重用提供有用的抽象,你可以使用 Livewire 的 Form 对象 来存储属性和验证规则。

下面是相同的 CreatePost 示例,但现在属性和规则已提取到名为 PostForm 的专用 form 对象:

php
<?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
<?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');
    }

    // ...
}

如你所见,我们可以使用 form 对象上的 ->all() 方法检索所有属性值,而不是单独列出每个属性。

此外,在模板中引用属性名称时,必须在每个实例前加上 form.

blade
<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>

使用 form 对象时,每次更新属性时都会运行 #[Validate] 属性验证。但是,如果你通过在属性上指定 onUpdate: false 来禁用此行为,则可以使用 $this->form->validate() 手动运行 form 对象的验证:

php
public function save()
{
    Post::create(
        $this->form->validate()
    );

    return redirect()->to('/posts');
}

Form 对象对于大多数较大的数据集来说是一个有用的抽象,并且具有各种附加功能,使它们更加强大。有关更多信息,请查看全面的 form 对象文档

实时验证

实时验证是指在用户填写表单时验证其输入,而不是等待表单提交时才验证。

通过直接在 Livewire 属性上使用 #[Validate] 属性,每当发送网络请求以更新服务器上的属性值时,都会应用提供的验证规则。

这意味着要为用户在特定输入上提供实时验证体验,不需要额外的后端工作。唯一需要的是使用 wire:model.livewire:model.blur 指示 Livewire 在填写字段时触发网络请求。

在下面的示例中,wire:model.blur 已添加到文本输入。现在,当用户在字段中输入,然后按 Tab 键或单击离开字段时,将使用更新的值触发网络请求,并运行验证规则:

blade
<form wire:submit="save">
    <input type="text" wire:model.blur="title">

    <!-- -->
</form>

如果你使用 rules() 方法为属性声明验证规则,而不是使用 #[Validate] 属性,你仍然可以包含一个没有参数的 #[Validate] 属性以保留实时验证行为:

php
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 的数据库字段,它代表"出生日期",你会希望向用户显示"The date of birth field is required"而不是"The dob field is required"。

Livewire 允许你使用 as: 参数为属性指定替代名称:

php
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: 参数自定义整个验证消息:

php
use Livewire\Attributes\Validate;

#[Validate('required', message: 'Please fill out your date of birth.')]
public $dob = '';

如果你有多个规则需要自定义消息,建议你为每个规则使用完全独立的 #[Validate] 属性,如下所示:

php
use Livewire\Attributes\Validate;

#[Validate('required', message: 'Please enter a title.')]
#[Validate('min:5', message: 'Your title is too short.')]
public $title = '';

如果你想使用 #[Validate] 属性的数组语法,可以像这样指定自定义属性和消息:

php
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 属性不支持的运行时语法(例如,Laravel 规则对象,如 Rule::password()),这会很有用。

然后,当你在组件内运行 $this->validate() 时,将应用这些规则。你还可以定义 messages()validationAttributes() 函数。

以下是一个示例:

php
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');
    }

    // ...
}

rules() 方法不会在数据更新时验证

当通过 rules() 方法定义规则时,Livewire 只会在你运行 $this->validate() 时使用这些验证规则来验证属性。这与标准的 #[Validate] 属性不同,后者每次通过 wire:model 等方式更新字段时都会应用。要在每次更新属性时应用这些验证规则,你仍然可以使用不带额外参数的 #[Validate]

不要与 Livewire 的机制冲突

在使用 Livewire 的验证工具时,你的组件不应该具有名为 rulesmessagesvalidationAttributesvalidationCustomValues 的属性或方法,除非你正在自定义验证过程。否则,这些将与 Livewire 的机制冲突。

使用 Laravel Rule 对象

Laravel Rule 对象是向表单添加高级验证行为的极其强大的方式。

以下是结合使用 Rule 对象和 Livewire 的 rules() 方法来实现更复杂验证的示例:

php
<?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 错误包

在 Form 对象中使用 $this->addError()

当在 form 对象内部使用 $this->addError 手动添加错误时,键将自动以 form 在父组件中分配到的属性名称作为前缀。例如,如果在组件中将 form 分配给名为 $data 的属性,键将变为 data.key

访问验证器实例

有时你可能希望访问 Livewire 在 validate() 方法中内部使用的 Validator 实例。这可以使用 withValidator 方法来实现。你提供的闭包接收完全构造的验证器作为参数,允许你在实际评估验证规则之前调用其任何方法。

以下是拦截 Livewire 的内部验证器以手动检查条件并添加额外验证消息的示例:

php
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 的验证功能,而是创建了一个完全自定义的验证器并将其应用于组件属性:

php
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
<?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 还允许你通过将要断言的规则作为方法的第二个参数传递来将断言缩小到特定规则:

php
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']]);
}

你还可以同时断言多个属性的验证错误存在:

php
public function test_cant_create_post_without_title_and_content()
{
    Livewire::test(CreatePost::class)
        ->call('save')
        ->assertHasErrors(['title', 'content']);
}

有关 Livewire 提供的其他测试工具的更多信息,请查看测试文档

在 JavaScript 中访问错误

Livewire 提供了一个 $errors 魔术属性,用于客户端访问验证错误:

blade
<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.first('field') - 获取字段的第一个错误消息
  • $errors.get('field') - 获取字段的所有错误消息
  • $errors.all() - 获取所有字段的所有错误
  • $errors.clear() - 清除所有错误
  • $errors.clear('field') - 清除特定字段的错误

使用 Alpine.js 时,通过 $wire.$errors 访问 $errors

已弃用的 [#Rule] 属性

当 Livewire v3 首次发布时,它使用术语"Rule"而不是"Validate"作为验证属性(#[Rule])。

由于与 Laravel 规则对象的命名冲突,此后已更改为 #[Validate]。Livewire v3 支持这两者,但建议你将所有出现的 #[Rule] 更改为 #[Validate] 以保持最新。