この記事は、お問合せフォームの確認画面をポップアップ(モーダル)で実装する方法の解説記事です。
今回は、LaravelとjQueryを使用して実装しました。
コードだけ見たい!という方は、こちら(GitHub)からどうぞ。
» Laravelで作成したアプリやアウトプットの一覧はこちら
スポンサーリンク
確認画面をポップアップ(モーダル)で実装するお問合せフォームのイメージ
スクリーンショットで紹介していきます。
今回は、Bootstrapで簡単にスタイルを作成しました。
お問い合わせページ
お問い合わせページのバリデーション
確認ページ(ポップアップ)
完了ページ
お問い合わせ受け付けメール
初期設定
初期設定は、お問い合わせフォームに限らず毎回実施することです。
Laravelのインストール
今回はLaravel6を使用しています。
composer create-project --prefer-dist laravel/laravel XXXX(フォルダ名) "6.*"
phpMyAdimnでDB作成
今回は「contact」という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
- 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 process()
{
}
public function complete()
{
return view('contacts.complete');
}
}
ルーティングの設定
routes/web.php
を編集します。
// お問い合わせ入力ページ
Route::get('/', 'ContactsController@index')->name('contact');
// DB挿入、メール送信
Route::post('/process', 'ContactsController@process')->name('process');
// 完了ページ
Route::get('/complete', 'ContactsController@complete')->name('complete');
ビューの作成
まず、resources/views
にcontactsフォルダを作成し、
- index
- complete
以上の2つのbladeテンプレート作成します。
フォームは「Laravel Collective」の「Forms & HTML」を使うので、インストールします。
※参考:Laravel Collective
composer require laravelcollective/html
index.blade.php
フォームに入力した内容を、「確認画面へ」を押したらモーダルに表示されるようにしています。
モーダルにはform
タグを設置し、入力内容をばりhidden
で持たせておいて、process
に送るようにしています。
ちょっと長いですが、以下になります。
<!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">
<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">
<label>{{ Form::checkbox('checkbox', "選択肢1") }}選択肢1</label>
<label>{{ Form::checkbox('checkbox', "選択肢2") }}選択肢2</label>
<label>{{ Form::checkbox('checkbox', "選択肢3") }}選択肢3</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">
<button type="button" class="btn btn-primary form-btn" data-toggle="modal" data-target="#exampleModalCenter">
確認画面へ
</button>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">確認画面</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{!! 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">
<p class="modal-name"></p>
<input class="modal-name" type="hidden" name="name" value="">
</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">
<p class="modal-email"></p>
<input class="modal-email" type="hidden" name="email" value="">
</div>
</div>
<div class="form-group row">
<p class="col-sm-4 col-form-label">電話番号</p>
<div class="col-sm-8">
<p class="modal-tel"></p>
<input class="modal-tel" type="hidden" name="tel" value="">
</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">
<p class="modal-gender"></p>
<input class="modal-gender" type="hidden" name="gender" value="">
</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">
<p class="modal-checkbox"></p>
<input class="modal-checkbox" type="hidden" name="checkbox" value="">
</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">
<p class="modal-contents"></p>
<input class="modal-contents" type="hidden" name="contents" value="">
</div>
</div>
<div class="text-center">
<button type="button" class="btn btn-secondary" data-dismiss="modal">修正する</button>
{{ Form::submit('送信', ['name' => 'submit', 'class' => 'btn btn-primary']) }}
</div>
{!! Form::close() !!}
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<script src="{{ asset('/js/main.js') }}"></script>
</body>
</html>
main.js
public/
配下にjsフォルダを作成し、読み込みます。
main.js
には、フォームに入力した内容をモーダルに表示する処理を書いています。
お問い合わせ内容は、確認画面とメール本文内でも改行できるようにしています。
チェックボックスは複数選択可なので、まずは配列に格納してから出力します。
$('.form-btn').click(function() {
const name = $('input[name="name"]').val();
const email = $('input[name="email"]').val();
const tel = $('input[name="tel"]').val();
const gender = $('input[name="gender"]:checked').val();
const contents = $('textarea[name="contents"]').val();
const inputContents = contents.replace( /\r?\n/g, '<br />' );
$('.modal-name').text(name).val(name);
$('.modal-email').text(email).val(email);
$('.modal-tel').text(tel).val(tel);
$('.modal-gender').text(gender).val(gender);
$('.modal-contents').html(inputContents).val(contents);
const checkbox = [];
$('input[name="checkbox"]:checked').each(function() {
checkbox.push($(this).val());
$('.modal-checkbox').text(checkbox).val(checkbox);
});
});
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/views
にlayout.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>
@yield('content')
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<script src="{{ asset('/js/main.js') }}"></script>
</body>
</html>
先ほど作成した以下のファイルに、埋め込んでいきます。
- index
- 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', 'checkbox', '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('checkbox');
$table->string('contents');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('contacts');
}
}
ここまで完了したら、以下のコマンドでDBにテーブルが作成されます。
php artisan migrate
DBにデータ保存、メール送信
app/Http/Controllers/ContactsController.php
のprocess
にDBにデータ保存、メール送信、入力画面に戻るボタンの動作を記述します。
public function process(Request $request)
{
$input = $request->except('submit');
if (isset($request->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["checkbox"] }}
お問い合わせ内容: {{ $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;
これでお問い合わせ入力→DB送信、メール送信まで完成です\(^o^)/
バリデーションの設定
最後にバリデーションを作っていきます。
ルールは以下のようにします。
- お名前:必須、全角10文字以内
- メールアドレス:必須、英数字のみ、@を含む
- 電話番号:半角数字のみ、ハイフンOK
- 性別:必須
- 選択:必須
- お問い合わせ内容:必須
main.js
に書いていきます。
ちょっと長くなりますが、以下のようになりました。
$('.form-btn').click(function() {
const name = $('input[name="name"]').val();
const email = $('input[name="email"]').val();
const tel = $('input[name="tel"]').val();
const gender = $('input[name="gender"]:checked').val();
const checkbox = $('input[name="checkbox"]:checked').val();
const contents = $('textarea[name="contents"]').val();
const inputContents = contents.replace( /\r?\n/g, '<br />' );
let error = false;
$('.attention-name').text('');
$('.attention-email').text('');
$('.attention-tel').text('');
$('.attention-gender').text('');
$('.attention-checkbox').text('');
$('.attention-contents').text('');
// name
// 全角
if (!name.match(/^[^\x01-\x7E\xA1-\xDF]+$/)) {
$('.attention-name').show().text('お名前は全角で入力してください。');
error = true;
}
// 10文字以内
if (name.length > 10) {
$('.attention-name').show().text('お名前は10文字以内で入力してください。');
error = true;
}
// 必須
if (name == '' || name == null) {
$('.attention-name').show().text('お名前は必須項目です。');
error = true;
}
// email
// 英数字のみ、@を含む
if (!email.match(/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/)) {
$('.attention-email').show().text('メールアドレスは正しく入力してください。');
error = true;
}
// 必須
if (email == '' || email == null) {
$('.attention-email').show().text('メールアドレスは必須項目です。');
error = true;
}
// tel
// 半角数字のみ(ハイフンOK)
if (tel.length > 0) {
if (!tel.match(/^[0-9\-]+$/)) {
$('.attention-tel').show().text('電話番号は半角数字で入力してください。');
error = true;
}
}
// gender
// 必須
if (gender == '' || gender == null) {
$('.attention-gender').show().text('性別は必須項目です。');
error = true;
}
// checkbox
// 必須
if (checkbox == '' || checkbox == null) {
$('.attention-checkbox').show().text('選択は必須項目です。');
error = true;
}
// contents
// 必須
if (contents == '') {
$('.attention-contents').show().text('お問い合わせ内容は必須項目です。');
error = true;
}
if (error == false) {
$('.form-btn').attr('data-toggle', 'modal');
$('.form-btn').attr('data-target', '#exampleModalCenter');
$('.modal-name').text(name).val(name);
$('.modal-email').text(email).val(email);
$('.modal-tel').text(tel).val(tel);
$('.modal-gender').text(gender).val(gender);
$('.modal-contents').html(inputContents).val(contents);
}
const inputCheckbox = [];
$('input[name="checkbox"]:checked').each(function() {
inputCheckbox.push($(this).val());
$('.modal-checkbox').text(inputCheckbox).val(inputCheckbox);
});
});
仕組みを簡単に解説すると、error
という変数を作り、バリデーションに引っ掛かったらtrue
にして、確認ボタンを押してもポップアップがでないようになっています。
error
がfalse
の場合は、
$('.form-btn').attr('data-toggle', 'modal');
$('.form-btn').attr('data-target', '#exampleModalCenter');
上記のように、Bootstrapのポップアップを出すためのデータ属性を付与するようにしています。
index.blade.php
で変更した部分は、以下のコードの<!– 追記 –>と<!– 変更 –>の部分です。
バリデーションのエラーメッセージを出す部分を追記し、確認ボタンのデータ属性を消しました。(エラーがない場合はJSで付与)
<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']) }}
<!-- 追記 -->
<p class="text-danger attention attention-name"></p>
</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']) }}
<!-- 追記 -->
<p class="text-danger attention attention-email"></p>
</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']) }}
<!-- 追記 -->
<p class="text-danger attention attention-tel"></p>
</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>
<!-- 追記 -->
<p class="text-danger attention attention-gender"></p>
</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::checkbox('checkbox', "選択肢1") }}選択肢1</label>
<label>{{ Form::checkbox('checkbox', "選択肢2") }}選択肢2</label>
<label>{{ Form::checkbox('checkbox', "選択肢3") }}選択肢3</label>
<!-- 追記 -->
<p class="text-danger attention attention-checkbox"></p>
</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']) }}
<!-- 追記 -->
<p class="text-danger attention attention-contents"></p>
</div>
</div>
<div class="text-center">
<!-- 変更 -->
<button type="button" class="btn btn-primary form-btn">
確認画面へ
</button>
</div>
あとはlayout.blade.php
に、
<style>
.attention {
display: none;
}
</style>
上記を書き、エラーがない場合はエラーメッセージ部分は非表示にしています。
以上で、お問合せフォームが完成です。
お疲れさまでした\(^o^)/
【まとめ】お問合せフォームの確認画面をポップアップ(モーダル)で実装する方法【Laravel】
今回は、以前作った「Laravelのお問合せフォーム」を元に、確認画面をポップアップで実装するという応用編を作ってみました。
コードだけ見たい!という方は、こちら(GitHub)からどうぞ。
以上です。
最後まで見てくださり、ありがとうございました。
Laravelでいろいろ作っていこうと思っているので、またアウトプットします。
» Laravelで作成したアプリやアウトプットの一覧はこちら
最後に宣伝
ココナラで、コーディングの相談を受け付けています。
- CSSが上手く作れない
- JavaScriptが思ったように動かない
- ブログのデザインを修正したい
- 勉強中でわからないところがあるから教えてほしい
このようなお悩みを解決していますので、「こんなの解決できる?」ということがあったら、ぜひ質問だけでも以下のリンクよりどうぞ。
コメント