【Laravel實戰】Livewire實作示範-表單驗證

【Laravel實戰】Livewire實作示範-表單驗證

快速實作

Livewire表單驗證

步驟1 . 建立 ContactForm 組件

首先我們先來建立 Livewire 組件,名為 ContactForm

php artisan livewire:make ContactForm

(此動作將生成 app/Http/Livewire/ContactForm.php 以及 resources/views/livewire/contact-form.blade.php)

步驟2 . 將表單移入組件視圖內

這一步驟,我們需要將原來視圖裡頭的表單移到組件內的視圖內,這樣才能夠得到組件類別的公開屬性資料

//resources\views\livewire\contact-form.blade.php

<div class="relative bg-white mt-8">
        <div class="absolute inset-0">
            <div class="absolute inset-y-0 left-0 w-1/2 bg-gray-50"></div>
        </div>
        <div class="relative max-w-7xl mx-auto lg:grid lg:grid-cols-5">
            <div class="bg-gray-50 py-16 px-4 sm:px-6 lg:col-span-2 lg:px-8 lg:py-24 xl:pr-12">
                <div class="max-w-lg mx-auto">
                    <h2 class="text-2xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-3xl sm:leading-9">
                        與哥布林聯絡
                    </h2>
                    <p class="mt-3 text-lg leading-6 text-gray-500">
                        Nullam risus blandit ac aliquam justo ipsum. Quam mauris volutpat massa dictumst amet. Sapien tortor
                        lacus arcu.
                    </p>
                    <dl class="mt-8 text-base leading-6 text-gray-500">
                        <div>
                            <dt class="sr-only">Postal address</dt>
                            <dd>
                                <p>新北市板橋區中山路一號</p>
                            </dd>
                        </div>
                        <div class="mt-6">
                            <dt class="sr-only">Phone number</dt>
                            <dd class="flex">
                                <svg class="flex-shrink-0 h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24"
                                    stroke="currentColor">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                        d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
                                </svg>
                                <span class="ml-3">
                                    +1 (555) 123-4567
                                </span>
                            </dd>
                        </div>
                        <div class="mt-3">
                            <dt class="sr-only">Email</dt>
                            <dd class="flex">
                                <svg class="flex-shrink-0 h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24"
                                    stroke="currentColor">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                        d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
                                </svg>
                                <span class="ml-3">
                                    info@goblinlab.org
                                </span>
                            </dd>
                        </div>
                    </dl>
                    <p class="mt-6 text-base leading-6 text-gray-500">
                        尋找工作機會?
                        <a href="#" class="font-medium text-gray-700 underline">檢視目前所有開放職缺</a>.
                    </p>
                </div>
            </div>
            <div class="bg-white py-16 px-4 sm:px-6 lg:col-span-3 lg:py-24 lg:px-8 xl:pl-12">
                <div class="max-w-lg mx-auto lg:max-w-none">
                    <form action="/send-mail" method="POST" class="grid grid-cols-1 row-gap-6">
                        @csrf

                        @if (session()->has('successMessage'))
                        <div class="rounded-md bg-green-50 p-4 mt-8">
                            <div class="flex">
                                <div class="flex-shrink-0">
                                    <svg class="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
                                        <path fill-rule="evenodd"
                                            d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
                                            clip-rule="evenodd" />
                                    </svg>
                                </div>
                                <div class="ml-3">
                                    <p class="text-sm leading-5 font-medium text-green-800">
                                        {{ session()->get('successMessage') }}
                                    </p>
                                </div>
                                <div class="ml-auto pl-3">
                                    <div class="-mx-1.5 -my-1.5">
                                        <button
                                            type="button"
                                            class="inline-flex rounded-md p-1.5 text-green-500 hover:bg-green-100 focus:outline-none focus:bg-green-100 transition ease-in-out duration-150"
                                            aria-label="Dismiss">
                                            <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                                                <path fill-rule="evenodd"
                                                    d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
                                                    clip-rule="evenodd" />
                                            </svg>
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                        @endisset

                        <div>
                            <label for="name" class="sr-only">姓名</label>
                            <div class="relative rounded-md shadow-sm">
                                <input id="name" name="name" value="{{ old('name') }}"
                                    class="@error('name')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="姓名">
                            </div>
                            @error('name')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror

                        </div>
                        <div>
                            <label for="email" class="sr-only">Email</label>
                            <div class="relative rounded-md shadow-sm">
                                <input id="email" type="text" name="email" value="{{ old('email') }}"
                                    class="@error('email')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="Email">
                            </div>
                            @error('email')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror
                        </div>
                        <div>
                            <label for="phone" class="sr-only">電話</label>
                            <div class="relative rounded-md shadow-sm">
                                <input id="phone" name="phone" value="{{ old('phone') }}"
                                    class="@error('phone')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="電話">
                            </div>
                            @error('phone')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror
                        </div>
                        <div>
                            <label for="message" class="sr-only">訊息</label>
                            <div class="relative rounded-md shadow-sm">
                                <textarea id="message" rows="4" name="message"
                                    class="@error('message')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="訊息">{{ old('message') }}</textarea>
                            </div>
                            @error('message')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror
                        </div>
                        <div class="">
                            <span class="inline-flex rounded-md shadow-sm">
                                <button type="submit"
                                    class="inline-flex items-center justify-center py-3 px-6 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out disabled:opacity-50">
                                    <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none"
                                        viewBox="0 0 24 24">
                                        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                                        <path class="opacity-75" fill="currentColor"
                                            d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
                                        </path>
                                    </svg>
                                    <span>提交</span>
                                </button>
                            </span>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

記得要保留最外層的·<div> ,因為要確保根元素只有一個。要不然雖然畫面呈現看似正常,實際上JS腳本是無法正常執行的

//resources/views/form-validation.blade.php

@extends('layouts.app')

@section('content')
    <div>
        <div class="h-96"></div>
        <div class="h-96"></div>
    </div>

    <h2 class="text-lg font-semibold">標準聯絡表單</h2>

    <livewire:contact-form />

@endsection

完成後,試看看頁面是否正常

步驟3 .編輯組件視圖

在這一個步驟,你不妨試看看為 wire:model 加入修飾子後綴,看看將會有怎樣的差異

  • wire:model.debounce.500ms (將 Ajax 請求間隔改為 500 微秒,預設為 250 微秒 )
  • wire:model.lazy (當輸入項取消專注時才會進行請求)
  • wire:model.defer (只有當表單提交時才會進行請求)
//resources\views\livewire\contact-form.blade.php

<div>
    <div class="relative bg-white mt-8">
        <div class="absolute inset-0">
            <div class="absolute inset-y-0 left-0 w-1/2 bg-gray-50"></div>
        </div>
        <div class="relative max-w-7xl mx-auto lg:grid lg:grid-cols-5">
            <div class="bg-gray-50 py-16 px-4 sm:px-6 lg:col-span-2 lg:px-8 lg:py-24 xl:pr-12">
                <div class="max-w-lg mx-auto">
                    <h2 class="text-2xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-3xl sm:leading-9">
                        與哥布林聯絡
                    </h2>
                    <p class="mt-3 text-lg leading-6 text-gray-500">
                        Nullam risus blandit ac aliquam justo ipsum. Quam mauris volutpat massa dictumst amet. Sapien tortor
                        lacus arcu.
                    </p>
                    <dl class="mt-8 text-base leading-6 text-gray-500">
                        <div>
                            <dt class="sr-only">Postal address</dt>
                            <dd>
                                <p>新北市板橋區中山路一號</p>
                            </dd>
                        </div>
                        <div class="mt-6">
                            <dt class="sr-only">Phone number</dt>
                            <dd class="flex">
                                <svg class="flex-shrink-0 h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24"
                                    stroke="currentColor">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                        d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
                                </svg>
                                <span class="ml-3">
                                    +1 (555) 123-4567
                                </span>
                            </dd>
                        </div>
                        <div class="mt-3">
                            <dt class="sr-only">Email</dt>
                            <dd class="flex">
                                <svg class="flex-shrink-0 h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24"
                                    stroke="currentColor">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                        d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
                                </svg>
                                <span class="ml-3">
                                    info@goblinlab.org
                                </span>
                            </dd>
                        </div>
                    </dl>
                    <p class="mt-6 text-base leading-6 text-gray-500">
                        尋找工作機會?
                        <a href="#" class="font-medium text-gray-700 underline">檢視目前所有開放職缺</a>.
                    </p>
                </div>
            </div>
            <div class="bg-white py-16 px-4 sm:px-6 lg:col-span-3 lg:py-24 lg:px-8 xl:pl-12">
                <div class="max-w-lg mx-auto lg:max-w-none">
                    <form wire:submit.prevent="submitForm" action="/send-mail" method="POST" class="grid grid-cols-1 row-gap-6">
                        @csrf

                        @if (session()->has('successMessage'))
                        <div class="rounded-md bg-green-50 p-4 mt-8">
                            <div class="flex">
                                <div class="flex-shrink-0">
                                    <svg class="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
                                        <path fill-rule="evenodd"
                                            d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
                                            clip-rule="evenodd" />
                                    </svg>
                                </div>
                                <div class="ml-3">
                                    <p class="text-sm leading-5 font-medium text-green-800">
                                        {{ session()->get('successMessage') }}
                                    </p>
                                </div>
                                <div class="ml-auto pl-3">
                                    <div class="-mx-1.5 -my-1.5">
                                        <button
                                            type="button"
                                            wire:click="$set('successMessage', null)"
                                            class="inline-flex rounded-md p-1.5 text-green-500 hover:bg-green-100 focus:outline-none focus:bg-green-100 transition ease-in-out duration-150"
                                            aria-label="Dismiss">
                                            <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                                                <path fill-rule="evenodd"
                                                    d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
                                                    clip-rule="evenodd" />
                                            </svg>
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                        @endisset

                        <div>
                            <label for="name" class="sr-only">姓名</label>
                            <div class="relative rounded-md shadow-sm">
                                <input wire:model.defer="name" id="name" name="name" value="{{ old('name') }}"
                                    class="@error('name')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="姓名">
                            </div>
                            @error('name')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror

                        </div>
                        <div>
                            <label for="email" class="sr-only">Email</label>
                            <div class="relative rounded-md shadow-sm">
                                <input wire:model.defer="email" id="email" type="text" name="email" value="{{ old('email') }}"
                                    class="@error('email')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="Email">
                            </div>
                            @error('email')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror
                        </div>
                        <div>
                            <label for="phone" class="sr-only">電話</label>
                            <div class="relative rounded-md shadow-sm">
                                <input wire:model.defer="phone" id="phone" name="phone" value="{{ old('phone') }}"
                                    class="@error('phone')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="電話">
                            </div>
                            @error('phone')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror
                        </div>
                        <div>
                            <label for="message" class="sr-only">訊息</label>
                            <div class="relative rounded-md shadow-sm">
                                <textarea wire:model.defer="message" id="message" rows="4" name="message"
                                    class="@error('message')border border-red-500 @enderror form-input block w-full py-3 px-4 placeholder-gray-500 transition ease-in-out duration-150"
                                    placeholder="訊息">{{ old('message') }}</textarea>
                            </div>
                            @error('message')
                            <p class="text-red-500 mt-1">{{ $message }}</p>
                            @enderror
                        </div>
                        <div class="">
                            <span class="inline-flex rounded-md shadow-sm">
                                <button type="submit"
                                    class="inline-flex items-center justify-center py-3 px-6 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out disabled:opacity-50">
                                    <svg wire:loading wire:target="submitForm" class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none"
                                        viewBox="0 0 24 24">
                                        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                                        <path class="opacity-75" fill="currentColor"
                                            d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
                                        </path>
                                    </svg>
                                    <span>提交</span>
                                </button>
                            </span>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

步驟4 .編輯 Livewire 組件類別

在這步驟,我們將要為組件類別加入公開屬性,使之能夠在組件視圖內被存取,並利用 reset() 來重置公開屬性。除此之外,我們先暫時移除表單驗證功能,等會再加回來

//app\Http\Livewire\ContactForm.php

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Mail\ContactFormMailable;
use Illuminate\Support\Facades\Mail;

class ContactForm extends Component
{
    public $name;
    public $email;
    public $phone;
    public $message;

    public function render()
    {
        return view('livewire.contact-form');
    }

    public function submitForm()
    {
        // $contact = $request->validate([
        //     'name' => 'required',
        //     'email' => 'required|email',
        //     'phone' => 'required',
        //     'message' => 'required',
        // ]);
        $contact['name'] = $this->name;
        $contact['email'] = $this->email;
        $contact['phone'] = $this->phone;
        $contact['message'] = $this->message;

        Mail::to('info@goblinlab.org')->send(new ContactFormMailable($contact));

        //重置表單
        $this->reset(['name', 'email', 'phone', 'message']);

        //返回並以 Session 來傳送訊息
        session()->flash('successMessage', '我們已經收到你的訊息,將盡快與你聯絡,感謝!');
    }
}

步驟5 .將訊息顯示改為透過公開屬性

在這一步驟,我們試著將訊息顯示的功能改從原先的 Session 存取改為透過公開屬性存取

//app\Http\Livewire\ContactForm.php

public $success_message;

public function submitForm()
    {
                ...
        $this->success_message = '我們已經收到你的訊息,將盡快與你聯絡,感謝!';
    }
//resources\views\livewire\contact-form.blade.php

@if($success_message)
<div class="rounded-md bg-green-50 p-4 mt-8">
    <div class="flex">
        <div class="flex-shrink-0">
            <svg class="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
                <path fill-rule="evenodd"
                    d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
                    clip-rule="evenodd" />
            </svg>
        </div>
        <div class="ml-3">
            <p class="text-sm leading-5 font-medium text-green-800">
                {{ $success_message }}
            </p>
        </div>
        <div class="ml-auto pl-3">
            <div class="-mx-1.5 -my-1.5">
                <button wire:click="$set('success_message',null)" type="button"
                    class="inline-flex rounded-md p-1.5 text-green-500 hover:bg-green-100 focus:outline-none focus:bg-green-100 transition ease-in-out duration-150"
                    aria-label="Dismiss">
                    <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd"
                            d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
                            clip-rule="evenodd" />
                    </svg>
                </button>
            </div>
        </div>
    </div>
</div>
@endif

步驟6 .加回驗證功能

現在我們將原先的驗證做法改為利用 Livewire 的驗證功能,透過 $this->validate()

//app\Http\Livewire\ContactForm.php

public function submitForm(){

    $contact = $this->validate([
        'name' => 'required',
        'email' => 'required|email',
        'phone' => 'required',
        'message' => 'required',
    ]);

        //$contact['name'] = $this->name;
    //$contact['email'] = $this->email;
    //$contact['phone'] = $this->phone;
    //$contact['message'] = $this->message;

    ...
}

步驟7 .改成即時驗證

所謂即時驗證指的是並非等到表單提交才驗證,而是在輸入項編輯過程中進入即時驗證。 流程有二: 1.建立 $rules 屬性 2.宣告 updated()

//app\Http\Livewire\ContactForm.php

protected $rules = [
    'name' => 'required',
    'email' => 'required|email',
    'phone' => 'required',
    'message' => 'required|min:5',
];

public function updated($propertyName)
{
    $this->validateOnly($propertyName);
}

public function submitForm(){

    $contact = $this->validate();

    ...
}

步驟8 . 加入運行動畫

在這一步驟,我們利用 Livewire 來實作運行動畫,透過加入以下流程

1.加入 wire:loading 2.加入 wire:target="submitForm"

//resources\views\livewire\contact-form.blade.php

<svg wire:loading wire:target="submitForm" class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
    xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
    <path class="opacity-75" fill="currentColor"
        d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
    </path>
</svg>

至此,我們已經完成了組件的表單驗證功能


功能測試

這個環節,我們來看看如何撰寫測試程式來測看看剛才我們所撰寫的功能

步驟 1.建立測試用例

所謂測試用例指的就是用來測試程式的程式碼

php artisan make:test ContactFormTest

步驟 2.編輯測試用例

在這一步,我們一一加入測試方法,要特別注意到每個方法的前綴必須以 test 開頭,否則是不會被加以呼叫執行的

//tests/Feature/ContactFormTest.php

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use Livewire\Livewire;
use App\Http\Livewire\ContactForm;
use App\Mail\ContactFormMailable;
use Illuminate\Support\Facades\Mail;

class ContactFormTest extends TestCase
{
    public function test_the_page_contain_contact_form_livewire_component()
    {
        $this->get('/form-validate')
            ->assertSeeLivewire('contact-form');
    }

    public function test_form_sends_out_an_email()
    {
        Mail::fake();

        Livewire::test(ContactForm::class)
            ->set('name', 'Goblin')
            ->set('email', 'demo@goblinlab.org')
            ->set('phone', '0911234567')
            ->set('message', 'Today is my day!!')
            ->call('submitForm')
            ->assertSee('我們已經收到你的訊息,將盡快與你聯絡,感謝!');

        Mail::assertSent(function (ContactFormMailable $mail) {
            $mail->build();

            return $mail->hasTo('info@goblinlab.org') &&
                $mail->hasFrom('demo@goblinlab.org') &&
                $mail->subject == '聯絡表單通知';
        });
    }

    public function test_form_name_field_is_required()
    {
        Livewire::test(ContactForm::class)
            ->set('email', 'demo@goblinlab.org')
            ->set('phone', '0911234567')
            ->set('message', 'Today is my day!!')
            ->call('submitForm')
            ->assertHasErrors(['name' => 'required']);
    }

    public function test_form_message_field_has_minium_chars()
    {
        Livewire::test(ContactForm::class)
            ->set('message', 'abc')
            ->assertHasErrors(['message' => 'min']);
    }
}

步驟 3.進行測試

要執行測試有多種方式,在這裡我為你介紹最簡單的一種,就是直接呼叫 test 命令

php artisan test

分享這篇文章:
 

關聯文章:

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

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

Laravel 百萬年薪特訓營

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