15分鐘搞懂Livewire組件驗證功能

關於驗證
在 Livewire 進行驗證非常類似於 Laravel 的標準表單驗證。簡單來說,Livewire 提供了 $rules 屬性用來設定每個組件的驗證規則,接著就能使用 $this->validate() 來利用這些規則去驗證組件屬性
以下是一個簡單的一個利用Livewire來進行表單驗證的例子
//app/Http/Livewire/ContactForm.php
class ContactForm extends Component
{
public $name;
public $email;
protected $rules = [
'name' => 'required|min:6',
'email' => 'required|email',
];
public function submit()
{
$this->validate();
// 以下如果驗證失敗的話就不會執行到
Contact::create([
'name' => $this->name,
'email' => $this->email,
]);
}
}
//resources/views/livewire/contact-form.blade.php
<form wire:submit.prevent="submit">
<input type="text" wire:model="name">
@error('name') <span class="error">{{ $message }}</span> @enderror
<input type="text" wire:model="email">
@error('email') <span class="error">{{ $message }}</span> @enderror
<button type="submit">Save Contact</button>
</form>
假如驗證失敗的話,一個標準的 ValidationException 將會被丟出並被 Livewire 抓到,而且一個標準的 $errors 物件能夠在組件的視圖內取得。因此任何你原先所寫過的Blade視圖檔案,用來處理驗證的部分都能直接拿來使用,不需大幅修改
此外,你也能夠在錯誤包內加入自定義的鍵與訊息,像這樣:
$this->addError('key', 'message’)
假如你需要動態的去定義驗證規則,你能夠將組件的 $rules 屬性修改成 rules() ,像這樣:
//app/Http/Livewire/ContactForm.php
class ContactForm extends Component
{
public $name;
public $email;
protected function rules()
{
return [
'name' => 'required|min:6',
'email' => ['required', 'email', 'not_in:' . auth()->user()->email],
];
}
}
即時驗證
有時候你可能會希望在使用者填完一個輸入項後,就立刻進行驗證,而不要等到提交表單時,這時你就需要即時驗證,而 Livewire 讓即時驗證功能變得很簡單,透過 $this->validateOnly()
為了在每一個更新時去驗證一個輸入項,我們可以使用 Livewire 的 updated hook:
//app/Http/Livewire/ContactForm.php
class ContactForm extends Component
{
public $name;
public $email;
protected $rules = [
'name' => 'required|min:6',
'email' => 'required|email',
];
public function updated($propertyName)
{
$this->validateOnly($propertyName);
}
public function saveContact()
{
$validatedData = $this->validate();
Contact::create($validatedData);
}
}
//resources/views/livewire/contact-form.blade.php
<form wire:submit.prevent="saveContact">
<input type="text" wire:model="name">
@error('name') <span class="error">{{ $message }}</span> @enderror
<input type="text" wire:model="email">
@error('email') <span class="error">{{ $message }}</span> @enderror
<button type="submit">Save Contact</button>
</form>
讓我們來分解一下究竟這個例子裡頭有哪些重點:
- 使用者輸入文字到 "name" 輸入項
- 當使用者輸入他們的名字,一個驗證錯誤訊息將會出現,告知他們名字短於6個字元以提醒其補完。假如使用者未改正就切換到輸入email,如果名字有錯誤的話仍然會出現提示名字錯誤的訊息
- 當使用者提交表單後,還會有一個最終的驗證確認,沒有問題的話才會存入資料庫
你可能會感到困惑,為什麼我會需要 validateOnly? 難道我不能使用 validate 就好嗎? 原因在於當單一的修改就要去驗證所有的欄位,這將會是惱人的用戶體驗。想像一下你才剛開始填入第一個欄位,結果突然間每個輸入項都跑出錯誤訊息,這是不是很困擾?
所以使用 validateOnly 就能避免這個問題,它只會驗證當前更新的輸入項
還有件事你可能不知道,其實你可以不透過 $rules 屬性來定義驗證規則,假如你因為某種原因不想要把驗證規則定義在 $rules 屬性中,你永遠可以直接把驗證規則直接傳入 validate() 以及 validateOnly()
//app/Http/Livewire/ContactForm.php
class ContactForm extends Component
{
public $name;
public $email;
public function updated($propertyName)
{
$this->validateOnly($propertyName, [
'name' => 'min:6',
'email' => 'email',
]);
}
public function saveContact()
{
$validatedData = $this->validate([
'name' => 'required|min:6',
'email' => 'required|email',
]);
Contact::create($validatedData);
}
}
自定義驗證錯誤訊息
假如你想要自定義用於 Livewire 組件的驗證訊息,你能夠透過 $messages 屬性。如果你想要沿用 Laravel 預設的驗證訊息,但只自定義訊息的 :attribute 部分,你能夠使用 $validationAttributes 屬性來定義屬性名稱
//app/Http/Livewire/ContactForm.php
class ContactForm extends Component
{
public $email;
protected $rules = [
'email' => 'required|email',
];
protected $messages = [
'email.required' => 'The Email Address cannot be empty.',
'email.email' => 'The Email Address format is not valid.',
];
protected $validationAttributes = [
'email' => 'email address'
];
public function updated($propertyName)
{
$this->validateOnly($propertyName);
}
public function saveContact()
{
$validatedData = $this->validate();
Contact::create($validatedData);
}
}
假如你沒有使用全域的驗證屬性 $rules ,那你可以直接將自定義訊息與屬性直接傳入 validate()
//app/Http/Livewire/ContactForm.php
class ContactForm extends Component
{
public $email;
public function saveContact()
{
$validatedData = $this->validate(
['email' => 'required|email'],
[
'email.required' => 'The :attribute cannot be empty.',
'email.email' => 'The :attribute format is not valid.',
],
['email' => 'Email Address']
);
Contact::create($validatedData);
}
}
改動錯誤訊息包
假如你不想那麼複雜,想要直接改動錯誤訊息包,可以嗎? 當然可以
validate() 和 validateOnly() 應該能夠處理大部分工作,但有時候你可能會閒的發慌想要自己去接手 Livewire 的內部錯誤包 Livewire 提供了一個有力的方法給你直接管理錯誤包,在每一個 Livewire 的組件類別內,你能夠呼叫以下方法
// 快速加入一個錯誤訊息到錯誤包內
$this->addError('email', 'The email field is invalid.’);
可以新增錯誤訊息,當然也要能夠清空錯誤訊息
//這裏個方法做相同的事情,也就是清除錯誤包
$this->resetErrorBag();
$this->resetValidation();
假如你只想要清除某個key的錯誤訊息,而非全部的。你能夠使用
$this->resetValidation('email');
$this->resetErrorBag('email');
下面這段程式將會給你完整的取用整個錯誤包
$errors = $this->getErrorBag();
而透過上面方式取得的錯誤包實例,你能夠做像這樣的事情
$errors->add('some-key', 'Some message');
驗證測試功能
你可能會想要去測試驗證功能是否正常,沒問題!
Livewire 提供了好用的驗證工具來幫助你測試驗證,就讓我們來寫一個聯絡單組件的簡單測試功能
/** @test */
public function name_and_email_fields_are_required_for_saving_a_contact()
{
Livewire::test('contact-form')
->set('name', '')
->set('email', '')
->assertHasErrors(['name', 'email']);
}
這看起來或許已經挺好用了,但我們還可以再更進一步來驗證某一個規則是否正常,看下一個例子
/** @test */
public function name_and_email_fields_are_required_for_saving_a_contact()
{
Livewire::test('contact-form')
->set('name', '')
->set('email', '')
->assertHasErrors([
'name' => 'required',
'email' => 'required',
]);
}
Livewire也提供了相反的驗證方法,比如 assertHasErrors() 之於 assertHasNoErrors()
/** @test */
public function name_field_is_required_for_saving_a_contact()
{
Livewire::test('contact-form')
->set('name', '')
->set('email', 'foo')
->assertHasErrors(['name' => 'required'])
->assertHasNoErrors(['email' => 'required']);
}
如果想要看關於這兩個方法的更多支援語法,請去參考 Laravel 官方的Testing 文件
自定義驗證器
假如你想要在Livewire內使用自定義的驗證系統,這絕對可行。 Livewire 將會抓取 ValidationException 並提供錯誤到視圖,就如同使用 $this->validate() 一般,請看這個例子
//app/Http/Livewire/ContactForm.php
use Illuminate\Support\Facades\Validator;
class ContactForm extends Component
{
public $email;
public function saveContact()
{
$validatedData = Validator::make(
['email' => $this->email],
['email' => 'required|email'],
['required' => 'The :attribute field is required'],
)->validate();
Contact::create($validatedData);
}
}
//resources/views/livewire/contact-form.blade.php
<div>
Email: <input wire:model.lazy="email">
@if($errors->has('email'))
<span>{{ $errors->first('email') }}</span>
@endif
<button wire:click="saveContact">Save Contact</button>
</div>
是不是和 Laravel 原生的做法非常類似呢?
既然如此,你可能會想是否可以使用Laravel的 FormRequest,根據 Livewire 的原先目的,要綁定在 Http 請求才能使用是不現實的,因此這樣的做法並不建議也不可能