【Laravel7】お問い合わせフォームの作成手順【確認/完了画面/メール送信】

CODE

この記事は、PHPのフレームワーク「Laravel」でお問い合わせフォームを作成するための解説記事です。

私(わや@wayasblog)自身、Laravelを最近勉強し始めたので、かなり丁寧に解説した初心者向けの記事となっています。

コードだけ見たい!という方は、こちら(GitHub)からどうぞ。

» Laravelで作成したアプリやアウトプットの一覧はこちら

スポンサーリンク

Laravelで作るお問い合わせフォームのイメージ

スクリーンショットで紹介していきます。
今回は、Bootstrapで簡単にスタイルを作成しました。

お問い合わせページ

お問い合わせページのバリデーション

確認ページ

完了ページ

お問い合わせ受け付けメール

初期設定

初期設定は、お問い合わせフォームに限らず毎回実施することです。

Laravelのインストール

composer create-project --prefer-dist laravel/laravel contact_form

phpMyAdimnでDB作成

今回は「contact」というDBを作成しました。

.envファイルの設定

DBとメールの設定のため、.envファイルを編集します。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=contact
DB_USERNAME=root
DB_PASSWORD=root

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=XXXXX
MAIL_PASSWORD=XXXXX
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=test@test.com
MAIL_FROM_NAME="${APP_NAME}"

今回メールは「MialTrap」を使用しています。

使い方は以下の記事を参考にしてみてください。
» メールの環境設定(ララ帳)

configファイルの設定

config/app.phpで日本語と時間の設定をします。

'timezone' => 'Asia/Tokyo',
'locale' => 'ja',

以上で、初期設定は終わりです。

コントローラーの作成

コントローラーの役割は、以下の通りです。

  • ルートから受け取った情報をモデルに処理をお願いする
  • モデルから受け取った情報をビューに表示する

具体的には、DBとの接続と処理、メール送信、バリデーション等を書いていきます。

php artisan make:controller ContactsController

※Laravelの規約に従うため、コントローラー名は必ず複数形にします。

app/Http/Controllers/ContactsController.phpが作成されました。

ここに、以下のfunctionを作っていきます。

  • index
  • confirm
  • process
  • complete

processは、DB挿入とメール送信のためのものでページは作成しないので、viewは渡しません。

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ContactsController extends Controller
{
    public function index()
    {
        return view('contacts.index');
    }

    public function confirm()
    {
        return view('contacts.confirm');
    }

    public function process()
    {
        
    }

    public function complete()
    {
        return view('contacts.complete');
    }
}

ルーティングの設定

routes/web.phpを編集します。

// お問い合わせ入力ページ
Route::get('/', 'ContactsController@index')->name('contact');

// 確認ページ
Route::post('/confirm', 'ContactsController@confirm')->name('confirm');

// DB挿入、メール送信
Route::post('/process', 'ContactsController@process')->name('process');

// 完了ページ
Route::get('/complete', 'ContactsController@complete')->name('complete');

ビューの作成

まず、resources/viewsにcontactsフォルダを作成し、

  • index
  • confirm
  • complete

以上の3つのbladeテンプレート作成します。

フォームは「Laravel Collective」の「Forms & HTML」を使うので、インストールします。
※参考:Laravel Collective

composer require laravelcollective/html

index.blade.php

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>お問い合わせ</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <h1 class="text-center mt-2 mb-5">お問い合わせ</h1>
        <div class="container">
            {!! Form::open(['route' => 'confirm', 'method' => 'POST']) !!}
                {{ csrf_field() }}
                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">お名前(10文字以内)<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        {{ Form::text('name', null, ['class' => 'form-control']) }}
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">メールアドレス<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        {{ Form::text('email', null, ['class' => 'form-control']) }}
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">電話番号</p>
                    <div class="col-sm-8">
                        {{ Form::text('tel', null, ['class' => 'form-control']) }}
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">性別<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        <label>{{ Form::radio('gender', "男性") }}男性</label>
                        <label>{{ Form::radio('gender', "女性") }}女性</label>
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">お問い合わせ内容<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        {{ Form::textarea('contents', null, ['class' => 'form-control']) }}
                    </div>
                </div>
                
                <div class="text-center">
                    {{ Form::submit('確認画面へ', ['class' => 'btn btn-primary']) }}
                </div>
            {!! Form::close() !!}
        </div>
    </div>
</body>
</html>

confirm.blade.php

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>お問い合わせ - 確認</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <h1 class="text-center mt-2 mb-5">お問い合わせ確認</h1>
        <div class="container">
            {!! Form::open(['route' => 'process', 'method' => 'POST']) !!}
                {{ csrf_field() }}
                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">お名前(10文字以内)<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">メールアドレス<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">電話番号</p>
                    <div class="col-sm-8">
                        
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">性別<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        
                    </div>
                </div>

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">お問い合わせ内容<span class="badge badge-danger ml-1">必須</span></p>
                    <div class="col-sm-8">
                        
                    </div>
                </div>
 
                <div class="text-center">
                    <button name="action" type="submit" value="return" class="btn btn-dark">入力画面に戻る</button>
                    <button name="action" type="submit" value="submit" class="btn btn-primary">送信</button>
                </div>
            {!! Form::close() !!}
        </div>
    </div>
</body>
</html>

complete.blade.php

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>お問い合わせ - 完了</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
    <div class="text-center">
        <h1 class="text-center mt-2 mb-5">お問い合わせありがとうございました。</h1>
        <a href="{{ route('contact') }}" class="btn btn-primary">お問い合わせ入力画面に戻る</a>
    </div>
</body>
</html>

レイアウトを共通化する

すべてのファイルに共通している部分を、共通化していきます。

resources/viewslayout.blade.phpを作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{{ $title }}</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        @yield('content')
    </div>
</body>
</html>

先ほど作成した以下のファイルに、埋め込んでいきます。

  • index
  • confirm
  • complete

たとえば、index.blade.phpの場合は、以下の通り。

@php
$title = 'お問い合わせ';
@endphp

@extends('layout')

@section('content')
    <h1 class="text-center mt-2 mb-5">お問い合わせ</h1>
        〜略〜
    </div>
@endsection

モデルとマイグレーションの作成

モデルはDBとの連携を行う機能で、マイグレーションはDBを管理する機能です。

php artisan make:model Contact -m

※Laravelの規約に従うため、モデル名は必ず単数形にします。そしてここの名前は、先ほど作成したDB名と統一しておきます。

app/Contact.phpが作成されるので、中身を書いていきましょう。

namespace App;

use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
    // Primary Key
    public $primaryKey = 'id';
    // Timestamps
    public $timestamps = true;

    protected $fillable = [
        'name',
        'email',
        'tel',
        'gender',
        'contents',
    ];
}

コマンドで-mを付けると、database/migrationsにマイグレーションファイルも一緒に作られるので、中身を書いていきます。

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateContactsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('contacts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->string('email');
            $table->string('tel')->nullable();
            $table->string('gender');
            $table->string('contents');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('contacts');
    }
}

ここまで完了したら、以下のコマンドでDBにテーブルが作成されます。

php artisan migrate

バリデーションの設定

app/Http/Controllers/ContactsController.phpconfirm の中に書いていきます。

public function confirm(Request $request)
    {
        $request->validate([
            'name'     => 'required|max:10',
            'email'    => 'required|email',
            'tel'      => 'nullable|numeric',
            'gender'   => 'required',
            'contents' => 'required',
        ]);

        return view('contacts.confirm');
    }

index.blade.phpにエラーを表示させます。

@php
$title = 'お問い合わせ';
@endphp

@extends('layout')

@section('content')
    <h1 class="text-center mt-2 mb-5">お問い合わせ</h1>
    <div class="container">
        {!! Form::open(['route' => 'confirm', 'method' => 'POST']) !!}
            {{ csrf_field() }}
            <div class="form-group row">
                <p class="col-sm-4 col-form-label">お名前(10文字以内)<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ Form::text('name', null, ['class' => 'form-control']) }}
                </div>
            </div>
            @if ($errors->has('name'))
                <p class="alert alert-danger">{{ $errors->first('name') }}</p>
            @endif

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">メールアドレス<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ Form::text('email', null, ['class' => 'form-control']) }}
                </div>
            </div>
            @if ($errors->has('email'))
                <p class="alert alert-danger">{{ $errors->first('email') }}</p>
            @endif

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">電話番号</p>
                <div class="col-sm-8">
                    {{ Form::text('tel', null, ['class' => 'form-control']) }}
                </div>
            </div>
            @if ($errors->has('tel'))
                <p class="alert alert-danger">{{ $errors->first('tel') }}</p>
            @endif

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">性別<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    <label>{{ Form::radio('gender', "男性") }}男性</label>
                    <label>{{ Form::radio('gender', "女性") }}女性</label>
                </div>
            </div>
            @if ($errors->has('gender'))
                <p class="alert alert-danger">{{ $errors->first('gender') }}</p>
            @endif

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">お問い合わせ内容<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ Form::textarea('contents', null, ['class' => 'form-control']) }}
                </div>
            </div>
            @if ($errors->has('contents'))
                <p class="alert alert-danger">{{ $errors->first('contents') }}</p>
            @endif
            
            <div class="text-center">
                {{ Form::submit('確認画面へ', ['class' => 'btn btn-primary']) }}
            </div>
        {!! Form::close() !!}
    </div>
@endsection

このままだと英語で表示されるので、日本語化していきます。

resources/langのenフォルダと同階層にjaフォルダを作成し、validation.phpをコピペ。

修正した箇所は以下の通りです。

'email' => ':attributeが正しくありません。',
'max' => [
        'string' => ':attributeは:max文字以内で入力してください。',
    ],
'numeric' => ':attributeは数字で入力してください。',
'required' => ':attributeは必須項目です。',

'attributes' => [
        'name'     => 'お名前',
        'email'    => 'メールアドレス',
        'tel'      => '電話番号',
        'gender'   => '性別',
        'contents' => 'お問い合わせ内容',
    ],

お問い合わせで入力した情報を、確認画面で表示する

app/Http/Controllers/ContactsController.phpconfirmに追記します。

public function confirm(Request $request)
    {
        $request->validate([
            'name'     => 'required|max:10',
            'email'    => 'required|email',
            'tel'      => 'nullable|numeric',
            'gender'   => 'required',
            'contents' => 'required',
        ]);

           // ここを追記
        // フォームから受け取ったすべてのinputの値を取得
        $inputs = $request->all();

        return view('contacts.confirm', ['inputs' => $inputs]);
    }

DBに挿入するために、confirm.blade.phphiddenでインプット内容を保持しておきます。

@php
$title = 'お問い合わせ - 確認';
@endphp

@extends('layout')

@section('content')
    <h1 class="text-center mt-2 mb-5">お問い合わせ確認</h1>
    <div class="container">
        {!! Form::open(['route' => 'process', 'method' => 'POST']) !!}
            {{ csrf_field() }}
            <div class="form-group row">
                <p class="col-sm-4 col-form-label">お名前(10文字以内)<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ $inputs['name'] }}
                </div>
            </div>
            <input type="hidden" name="name" value="{{ $inputs['name'] }}">

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">メールアドレス<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ $inputs['email'] }}
                </div>
            </div>
            <input type="hidden" name="email" value="{{ $inputs['email'] }}">

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">電話番号</p>
                <div class="col-sm-8">
                    {{ $inputs['tel'] }}
                </div>
            </div>
            <input type="hidden" name="tel" value="{{ $inputs['tel'] }}">

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">性別<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ $inputs['gender'] }}
                </div>
            </div>
            <input type="hidden" name="gender" value="{{ $inputs['gender'] }}">

            <div class="form-group row">
                <p class="col-sm-4 col-form-label">お問い合わせ内容<span class="badge badge-danger ml-1">必須</span></p>
                <div class="col-sm-8">
                    {{ $inputs['contents'] }}
                </div>
            </div>
            <input type="hidden" name="contents" value="{{ $inputs['contents'] }}">
            
            <div class="text-center">
                <button name="action" type="submit" value="return" class="btn btn-dark">入力画面に戻る</button>
                <button name="action" type="submit" value="submit" class="btn btn-primary">送信</button>
            </div>
        {!! Form::close() !!}
    </div>
@endsection

DBにデータ保存、メール送信

app/Http/Controllers/ContactsController.phpprocessにDBにデータ保存、メール送信、入力画面に戻るボタンの動作を記述します。

public function process(Request $request)
    {
        $action = $request->get('action', 'return');
        $input  = $request->except('action');

        if($action === 'submit') {

            // DBにデータを保存
            $contact = new Contact();
            $contact->fill($input);
            $contact->save();

            // メール送信
            Mail::to($input['email'])->send(new ContactMail('mails.contact', 'お問い合わせありがとうございます', $input));

            return redirect()->route('complete');
        } else {
            return redirect()->route('contact')->withInput($input);
        }
    }

Mailableクラス作成

メール送信のためのMailableクラスを作成します。

php artisan make:mail ContactMail

これで、app/Mail/ContactMail.phpが作成されるので、編集していきます。

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class ContactMail extends Mailable
{
    use Queueable, SerializesModels;

    public $view;
    public $subject;
    public $data;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($template, $subject, $data)
    {
        $this->template = $template;
        $this->subject  = $subject;
        $this->data     = $data;
    }

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

resources/views/mails/contact.blade.phpを作成し、送信するメールを書いていきます。

{{ $data["name"] }} 様

お問い合わせありがとうございます。
以下の内容で受け付けました。

=================
お名前: {{ $data["name"] }}

メールアドレス: {{ $data["email"] }}

電話番号: {{ $data["tel"] }}

性別: {{ $data["gender"] }}

お問い合わせ内容: {{ $data["contents"] }}
=================

app/Http/Controllers/ContactsController.phpにuse宣言も忘れずに書きましょう。

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use App\Contact;
use App\Mail\ContactMail;

以上で、お問合せフォームが完成です。
お疲れさまでした\(^o^)/

【まとめ】お問い合わせフォームの作成手順【Laravel7】

初めてLaravelでお問合せフォームを作ってみたので、アウトプットしてみました。

コードだけ見たい!という方は、こちら(GitHub)からどうぞ。

Laravelの基礎的な知識は、以下のYouTube動画にお世話になりました。

2つ目は英語ですが、コードを見ながらであれば理解できると思います。

以上です。
最後まで見てくださり、ありがとうございました。

Laravelでいろいろ作っていこうと思っているので、またアウトプットします。

» Laravelで作成したアプリやアウトプットの一覧はこちら

最後に宣伝

ココナラで、コーディングの相談を受け付けています。

  • CSSが上手く作れない
  • JavaScriptが思ったように動かない
  • ブログのデザインを修正したい
  • 勉強中でわからないところがあるから教えてほしい

このようなお悩みを解決していますので、「こんなの解決できる?」ということがあったら、ぜひ質問だけでも以下のリンクよりどうぞ。

HTML / CSS / JSのお悩みを解決します コーディングでお困りの方はお気軽にお問い合わせください! | Webサイト修正・カスタム・コンサル | ココナラ
HTML / CSS / JavaScript(jQuery)のお悩み相談、ご質問を受け付けます。「CSSが上手く作れない」「JavaScriptが思ったように...

コメント

タイトルとURLをコピーしました