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

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 請求才能使用是不現實的,因此這樣的做法並不建議也不可能


分享這篇文章:

關聯文章:

訂閱電子報,索取 Laravel 學習手冊

價值超過 3000 元,包含常用 Laravel 語法與指令!

一小時免費求職講座

3個應徵軟體工程師前該知道的秘訣

取得免費課程連結

Laravel 百萬年薪特訓營

從最基礎的 PHP 語法開始,包含所有你該知道的網頁基礎知識,連同 Laravel 從零開始一直到實戰,最後還將告訴你如何找好工作,讓你及早擁有百萬年薪