📒

Laravel 8 + Livewire でメールフォームを作るチュートリアル

2021/02/19に公開

Laravel 8 と Livewireを使って、メールフォームを作る機会がありましたので、その作成手順をシェアします。

フォームはシンプルな、入力、確認、完了の3画面の一般的なお問い合わせメールフォームです。

完成したソースコード

テンプレートリポジトリとしてgithubにおきました。

https://github.com/hdmt/livewire-contact-template

開発環境

Laravel Sailを使ったMac+Docker環境を用意します。構築手順は以下の記事のとおりです。

https://zenn.dev/hdmt/articles/132a5014bda7ad

上記で作ったLaravelに Livewire をインストールします。
プロジェクトディレクトリ直下で次のコマンドを実行します。

./vendor/bin/sail composer require livewire/livewire

以上で開発の準備が整いました。

Livewireコンポーネント作成

お問い合わせフォームの入力、確認、完了の3画面にあたるLivewireコンポーネントを作成します。

// 入力画面
./vendor/bin/sail php artisan make:livewire Input
// 確認画面
./vendor/bin/sail php artisan make:livewire Confirm
// 完了画面
./vendor/bin/sail php artisan make:livewire Complete

上のコマンドにより、livewireのクラスとbladeファイルのひな形が生成されます。

ルーティングの設定

次に、お問い合わせフォームの入力、確認、完了の3画面について、ルーティングを設定します。

routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Livewire\Input;
use App\Http\Livewire\Confirm;
use App\Http\Livewire\Complete;

Route::get('/', Input::class)->name('home'); //入力画面
Route::get('/confirm', Confirm::class)->name('confirm'); //確認画面
Route::get('/complete', Complete::class)->name('complete'); //完了画面

入力画面のコンポーネント実装

まず、入力画面を制御する、Inputコンポーネントを実装します。

ビューで扱う3つの変数 $posts$requestList$prefectures をプロパティとして定義します。

Livewireに定められた特別のメソッド(ライフサイクルフック)の一つ、mountメソッドでこれらの変数に値を設定することで、ビューに値を渡せるようになります。

app/Http/Livewire/Input.php
class Input extends Component
{
    public $posts; // ユーザが入力する値
    public $requestList; // チェックボックスで表示するリスト
    public $prefectures; // selectで表示する都道府県のリスト
    
    public function mount()
    {
	// チェックボックスで表示すデータをconfigファイルから取得する
        $this->requestList = config('contact.requests');
	// チェックボックスで表示すデータをconfigファイルから取得する
        $this->prefectures = config('contact.prefectures');
	// 確認画面から入力に戻ったときのため、sessionに保存した入力値を取得
        $this->posts = session()->get('posts');
    }

ビューを表示するrender()メソッドは、make:livewire コマンドで生成されたときのデフォルトから変更せず、そのまま使用します。

app/Http/Livewire/Input.php
public function render()
{
	return view('livewire.input');
}

チェックボックス、都道府県のselectタグに表示する情報は、configファイルにそれぞれ定義して、configヘルパで取得します。

config/contact/requests.php
return [
    '資料・サンプルが欲しい',
    '価格が知りたい',
    'その他'
];
config/contact/prefectures.php
return [
  '1' => '北海道',
  '2' => '青森県',
   ()
  '47' => '沖縄県',
];

ビュー作成

まずlayoutのbladeを作り、$slotでLivewireのコンポーネントがレンダーされます。

resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    @livewireStyles
</head>
<body class="antialiased overflow-x-hidden">

    {{ $slot }}

    @livewireScripts
</body>

次に入力画面のformのbladeを作成します。

formタグには、livewireのディレクティブでイベントとアクションをwire:submit.prevent="confirm" のように指定しています。

ディレクティブの形式は、wire:[ディスパッチされたブラウザイベント]="[アクション]"です。

これでsubmitされたときに、あとで Livewire/Input.php に実装するconfirmメソッドが実行れます。

inputタグには、wire:model="posts.name"を指定します。

resources/views/livewire/input.blade.php
<form wire:submit.prevent="confirm">
@csrf
    <div>
        <div>
            <span >お名前(必須)</span>
            <input
                wire:model="posts.name"
                type="text" placeholder="鈴木一郎">
            @error('posts.name') <span>{{ $message }}</span> @enderror
        </div>
        (略)
	<button>内容確認</button>
    </div>
</form>

完成版コード

ここまでで、次ような入力画面が http://localhost/ で表示できるようになりました。

内容確認ボタン押下時の処理

つづいて、入力内容の確認ボタンを押下した時の処理を実装します。

入力値のバリデーションのため、Inputクラスにバリデーションルールとエラーメッセージを追加します。

app/Http/Livewire/Input.php
class Input extends Component
{
    (略)
    
    protected $rules = [
	// name を必須
        'posts.name' => 'required',
	// email を必須 かつ メールアドレス形式
        'posts.mail' => 'required|email',
	// request(チェックボックスの選択)を必須 かつ 配列形式
        'posts.request' => 'required|array',
    ];

  // エラーメッセージの設定
    protected $messages = [
        'posts.*.required' => '必須項目です',
        'posts.mail.email' => '正しいメールアドレスを入力ください',
    ];

確認ボタンが押されたときに、実行するconfirm()メソッドを次のように記述します。

app/Http/Livewire/Input.php
public function confirm()
{
	// バリデーションの実行
	$this->validate();

	// 入力されたpostプロパティを、セッション名'posts'で
	// セッションに保存
	session()->put('posts', $this->posts);

	// 確認画面へリダイレクト
	return redirect()->route('confirm');
}

Inputクラスの完成版コード

ここまでで、入力画面が完成です。

確認画面のコンポーネント

確認画面のLivewireコンポーネントは、mount()で次のように実装します。
セッションに保持した入力情報を、session()->get('posts')で取得し、確認画面のビューに渡すようにします。

app/Http/Livewire/Confirm.php
・・・
class Confirm extends Component
{

    public $posts;
    public $requestList;
    public $prefectures;

    public function mount()
    {
        $this->requestList = config('contact.requests');
        $this->prefectures = config('contact.prefectures');
	// sessionに保存した入力値を取得
        $this->posts = session()->get('posts');
	// 入力なしで確認画面に直接アクセスがあったらhomeへリダイレクト
        if(empty($this->posts)){
            return redirect()->route('home');
        }
    }
・・・

メール送信処理

メール送信ボタン押下時に実行する submit()メソッドを実装します。
$recipientsにユーザが入力したメールアドレスと、configファイルに設定した管理者アドレスを格納し、Mailableクラスを使って、それぞれに送信を実行します。

app/Http/Livewire/Confirm.php
・・・
use Illuminate\Support\Facades\Mail;
use App\Mail\Contact;
・・・
    public function submit()
    {
        // メール送信
        $recipients = [ 
            Arr::get($this->posts, 'mail'),
            config('app.admin_address')
        ];
        foreach ($recipients as $recipient) {
            Mail::to($recipient)->send(new Contact($this->posts));
        }

        // 完了画面へ
        return redirect()->route('complete');
    }

・・・

完成版コード

Mailableクラス

Mailableクラスをmakeコマンドで生成します。

sail php artisan make:mail Contact

コンストラクタで入力内容など、メール本文に渡したいプロパティを定義し、build()メソッドでメール本文のビューを指定します。

app/Mail/Contact.php
class Contact extends Mailable
{
    use Queueable, SerializesModels;

    public $posts;
    public $requestList;
    public $prefectures;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($posts)
    {
        $this->requestList = config('contact.requests');
        $this->prefectures = config('contact.prefectures');
        $this->posts = $posts;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->text('email.contact');
    }
}

メール本文は、次のようにビューを用意します。
それぞれ取得したプロパティを表示させるようにします。

resources/views/email/contact.blade.php
■お名前
{{ Arr::get($posts, 'name') }}

■メールアドレス
{{ Arr::get($posts, 'mail') }}

■ご要望
@foreach ($posts['request'] as $request)
{{$requestList[$request]}}
@endforeach

■郵便番号
{{ Arr::get($posts, 'zip', '-') }}

■都道府県
{{ @$prefectures[ $posts['prefecture'] ] }}

■ご希望・ご質問
{{ Arr::get($posts, 'comment', '-') }}

.envの設定

メールの送信元や、管理用アドレスの設定を、.envに以下のとおり記載します。

.env
・・・
MAIL_FROM_ADDRESS=from@example.com
MAIL_FROM_NAME="${APP_NAME}"
MAIL_TO_ADDRESS=to@example.com
・・・

メール送信結果

Laravel Sailで構築した環境にデフォルトで付属するメール送信ツール・MailHogで、メールの送信結果を確認します。

MailHogはhttp://localhost:8025/ でアクセスできます。

確認画面のblade

確認画面では、セッションに保存したpostsプロパティのデータを表示します。

resources/views/livewire/confirm.blade.php

// nameの表示
<dd>{{ @$posts['name'] }}</dd>

// チェックボックス選択したrequestの表示 
@foreach ($posts['request'] as $request)
{{$requestList[$request]}}<br>
@endforeach

// selectボックス選択した都道府県の表示
{{ @$prefectures[ $posts['prefecture'] ] }}

完成版のコード

確認画面の表示イメージ

完了画面のコンポーネント

完了画面のコンポーネントでやることはシンプルです。
次のとおり、mountでセッションのチェックと削除、ビューの表示のみを行っています。

app/Http/Livewire/Complete.php
(略)
class Complete extends Component
{
    public function mount()
    {
	// 完了画面に直接アクセスされたときの対策
	// sessionにデータがなければ、homeへ戻す
        if(empty(session()->get('posts'))){
            return redirect()->route('home');
        }

        // セッションクリア
        session()->flush();
    }
    public function render()
    {
        return view('livewire.complete');
    }
}

完成版のコード

完了画面のblade

ビューは、送信完了のメッセージを表示します。

resources/views/livewire/complete.blade.php
<div>送信が完了しました。<br>お問い合わせいただき、ありがとうございました。</div>

完了画面の表示イメージ

おわりに

以上、Laravel と Livewire でメールフォームを作るチュートリアルをご紹介しました。
内容に誤りや不足があれば、コメントでご指摘いただけるとうれしいです。

参考

https://readouble.com/livewire/2.x/ja/quickstart.html

https://readouble.com/livewire/2.x/ja/lifecycle-hooks.html

https://readouble.com/laravel/8.x/ja/mail.html

Discussion