Laravelでフォームを作る

2024-07-30

laravel
Example Image

Laravelを使ってお問い合わせフォームを作ります。

バリデーションはLaravelのコントローラー、フォームの状態管理はinertiaのuseFormで行います。

設置するサーバーのメール設定に合わせて、.envに必要な情報を入力しておきます。

ルーティング

web.phpにフォームの表示・送信に関するルーティングを作ります。

Route::get('/contact', [ContactController::class, 'request'])->name('contact.request');
Route::post('/contact', [ContactController::class, 'send'])->name('contact.send');

コントローラー

ContactController.phpを生成しメソッドを追加します。

<?php

namespace App\Http\Controllers;

use App\Http\Requests\ContactFormRequest;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Inertia\Inertia;
use Inertia\Response;
use App\Mail\ContactSendmail;
use Illuminate\Support\Facades\Mail;

class ContactController extends Controller
{
    public function request(): Response
    {
        return Inertia::render('ContactForm');
    }

    public function send(ContactFormRequest $request): RedirectResponse
    {
        $contact = $request->all();

        // ユーザーのアドレスを取得
        $userAddress = $contact['email'];
        
        // 環境変数から送信元アドレスを取得
        $fromAddress = env('MAIL_FROM_ADDRESS');

        // メールを送信
        Mail::to($userAddress)->cc($fromAddress)->send(new OrderShipped($contact));

        // セッションのトークンを再生成
        $request->session()->regenerateToken();

        return redirect()->route('contact.request');
    }
}

requestはフォームを表示します。

sendはフォームからPOST通信で受け取り、Mailableクラスを使ってメールの送信処理を行います。

リクエスト

contactFormRequest.phpを生成し、POST通信で受け取るデータにバリデーションを追加します。

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ContactFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'name' => 'required',
            'email' => 'required|email',
            'content' => 'required',
        ];
    }
}

ビュー

resources/Pages/ContactForm.tsxを作成します。

import GuestLayout from "../Layouts/GuestLayout";
import InputError from "../Components/InputError";
import PrimaryButton from "../Components/PrimaryButton";
import TextInput from "../Components/TextInput";
import Textarea from "../Components/Textarea";
import InputLabel from "../Components/InputLabel";
import { Head, useForm } from "@inertiajs/react";
import { FormEventHandler } from "react";
import { Transition } from "@headlessui/react";

export default function ContactForm() {
    const { data, setData, post, processing, errors, recentlySuccessful } =
        useForm({
            name: "",
            email: "",
            content: "",
        });

    const submit: FormEventHandler = (e) => {
        e.preventDefault();

        post(route("contact.send"));
    };

    return (
        <GuestLayout>
            <Head title="お問い合わせ" />

            <div className="my-4 text-2xl text-center text-gray-600">
                お問い合わせフォーム
            </div>
            <form onSubmit={submit}>
                <InputLabel htmlFor="name" value="お名前" />
                <TextInput
                    id="name"
                    type="text"
                    name="name"
                    value={data.name}
                    className="mt-1 block w-full"
                    isFocused={true}
                    onChange={(e) => setData("name", e.target.value)}
                />
                <InputError message={errors.name} className="mt-2 text-red" />
                <InputLabel
                    htmlFor="email"
                    value="メールアドレス"
                    className="mt-4"
                />
                <TextInput
                    id="email"
                    type="email"
                    name="email"
                    value={data.email}
                    className="mt-1 block w-full"
                    isFocused={true}
                    onChange={(e) => setData("email", e.target.value)}
                />
                <InputError message={errors.email} className="mt-2 text-red" />
                <InputLabel
                    htmlFor="content"
                    value="お問い合わせ内容"
                    className="mt-4"
                />
                <Textarea
                    id="content"
                    name="content"
                    value={data.content}
                    className="mt-1 block w-full"
                    isFocused={true}
                    onChange={(e) => setData("content", e.target.value)}
                />
                <InputError
                    message={errors.content}
                    className="mt-2 text-red"
                />
                <div className="flex items-center justify-end mt-8">
                    <Transition
                        show={recentlySuccessful}
                        enterFrom="opacity-0"
                        leaveTo="opacity-0"
                        className="transition ease-in-out"
                    >
                        <p className="text-sm text-gray-600">送信しました!</p>
                    </Transition>
                    <PrimaryButton className="ms-4" disabled={processing}>
                        送信する
                    </PrimaryButton>
                </div>
            </form>
        </GuestLayout>
    );
}

inputが空欄のまま、またはemailの値がメールアドレスでない場合、ボタンを押しても送信されずエラーメッセージが表示されます。

送信が成功するとボタン横にフラッシュメッセージが表示されます。

自動送信メールの本文はresourses/views/mail.blade.phpに作成します。

<!DOCTYPE html>
<html>
<head>
    <title>お問い合わせありがとうございます</title>
</head>
<body>
    <h1>お問い合わせありがとうございます!</h1>
    <p>内容を確認し返信しますのでお待ちください。</p>
    <p>------------------------------------------</p>
    <p><strong>Name:</strong> {{ $name }} さま</p>
    <p><strong>Email:</strong> {{ $email }}</p>
    <p><strong>Message:</strong> {{ $content }}</p>
</body>
</html>

Malableクラス

最後に送信処理を行うクラスを生成します。

sail artisan make:mail OrderShipped

生成されたファイルは以下のように記述します。

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
use Illuminate\Mail\Mailables\Address;

class OrderShipped extends Mailable
{
    use Queueable, SerializesModels;

    // メールに関するデータを保存するためのプライベートプロパティ
    private $name;
    private $email;
    private $content;

    /**
     * Create a new message instance.
     * $contact配列からname, email, contentを取り出してMailクラスのプロパティにセット
     */
    public function __construct($contact)
    {
        //
        $this->name = $contact['name'];
        $this->email = $contact['email'];
        $this->content = $contact['content'];
    }

    /**
     * Get the message envelope.
     * メールの送信者、返信先、件名などを設定するメソッド
     */
    public function envelope(): Envelope
    {
        $fromAddress = env('MAIL_FROM_ADDRESS');

        return new Envelope(
            from: new Address($fromAddress, 'risu'),
            replyTo: [
                new Address($this->email, $this->name)
        ],
            subject: 'Contact Sendmail',
        );
        
    }

    /**
     * Get the message content definition.
     * メールの内容を設定するメソッド(viewはmail.blade.php、withはviewに渡すデータ)
     */
    public function content(): Content
    {
        return (new Content())->view('mail')
                          ->with([
                              'name' => $this->name,
                              'email' => $this->email,
                              'content' => $this->content,
                          ]);
    }

これでお問い合わせフォームの完成です!
デプロイして送信すると自動返信メールが届きます。

share