【超解説】Laravelでお問い合わせフォーム作成!Gmailで連携するには?
この記事からわかること
- Laravelでお問い合わせフォームを作成する方法
- Gmailを使うには?
- テスト送信や作成の流れを解説
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
Laravelでお問い合わせフォームを作成する際のロジックや仕組み、注意点、そしてメールアドレスにGmailを使用する場合の方法をLaravelを学習したての初心者向けに詳しくまとめて行きたいと思います。
コピペだけでなく意味を理解してちゃんと使えるようになるのが目標です!
またLaravelのお問い合わせフォームのロジック部分及びコードは以下リンクの記事を参考にさせて頂きました。(心優しい方で引用許可をいただきました。)
参考記事:【Laravel】確認画面付きのお問い合わせフォームを作成する【メール送信対応】
Laravelでお問い合わせフォームを作ろう
まず最初にLaravelプロジェクト内の編集するファイルと新しく作成するファイルを整理しておきます。
編集するファイル
├── Laravelプロジェクト
│ ├── routes
│ └── ●web.php
│ └── ●.env
作成するファイル
├── Laravelプロジェクト
│ ├── app
│ ├── Http
│ └── Controllers
│ └── ★ContactsController.php
│ └── Mail
│ └── ★ContactsSendmail.php
│
│
│ ├── resource
│ └── view
│ └── contact
│ ├── ●index.blade.php
│ ├── ●confirm.blade.php
│ ├── ●mail.blade.php
│ └── ●thanks.blade.php
上記のファイルがお問い合わせフォームを作る上で必要になってきます。★マークがついたものはターミナルからartisan(アルチザン)コマンドを使用して作成します。それ以外はエディタなどから随時ファイルを作成すればOKです。
$ php artisan make:controller ContactsController
$ php artisan make:mail ContactsSendmail
作成するためにやるべきこと
これからやることの流れを確認しておきます。
- ルーティング
- HTMLフォームの実装
- コントローラーでの処理
- メール設定とフォーマットの作成
- .envファイルへのメール設定の更新
- Gmailを扱えるようにする
ルーティング処理
記述するファイル
│ ├── routes
│ └── ●web.php
まずはLaravelの命でもあるルーティング処理を記述します。定義するのは3つ。「入力フォームページ」「確認フォームページ」「送信完了ページ」です。
//入力フォームページ
Route::get('/contact', 'ContactsController@index')->name('contact.index');
//確認フォームページ
Route::post('/contact/confirm', 'ContactsController@confirm')->name('contact.confirm');
//送信完了ページ
Route::post('/contact/thanks', 'ContactsController@send')->name('contact.send');
ルーティングはRoute
の後にHTTPメソッド(getやpost)、アクセスされるURL、アクセスされた時の処理(今回はコントローラーのアクションメソッド)、このルーティングの名前を定義することができます。
アクションメソッド(ContactsController@以降の部分)の名前はそれぞれindex、confirm、sendとしておきます。
HTTPメソッドはindexページはget
、それ以外はフォームが送信されたページになるのでpostにしておきます。
それぞれのフォーム部分の実装
記述するファイル
│ ├── resource
│ └── view
│ └── contact
│ ├── ●index.blade.php // 入力フォームページ
│ ├── ●confirm.blade.php // 入力確認ページ
│ ├── ●mail.blade.php // メール送信時の本文部分
│ └── ●thanks.blade.php // 送信完了ページ
「resource」>「view」内に「contact」フォルダを作成し、その中に4つのファイルを作成します。「mail.blade.php」以外は@sectionや@import、@extends
などを使ってレイアウトや描写を作ってもらうとしてロジック部分だけに焦点を当ててみていきます
入力フォームページ(index.blade.php)
お問い合わせフォームに内容を入力してもらうページです。
重要なポイント
- アクション先にルーティングの名前
- @csrfディレクティブ
- oldヘルパ関数
- $errors変数
@section('content')
<form method="POST" action="{{ route('contact.confirm') }}">
@csrf
<label>メールアドレス</label>
<input
name="email"
value="{{ old('email') }}"
type="text">
@if ($errors->has('email'))
<p class="error-message">{{ $errors->first('email') }}</p>
@endif
<label>タイトル</label>
<input
name="title"
value="{{ old('title') }}"
type="text">
@if ($errors->has('title'))
<p class="error-message">{{ $errors->first('title') }}</p>
@endif
<label>お問い合わせ内容</label>
<textarea name="body">{{ old('body') }}</textarea>
@if ($errors->has('body'))
<p class="error-message">{{ $errors->first('body') }}</p>
@endif
<button type="submit">入力内容確認</button>
</form>
@endsection
アクション先にルーティングの名前
<form method="POST" action="{{ route('contact.confirm') }}">
action先にはURLを指定しますが、Laravelではルーティング処理でつけた名前でも指定可能です。ここではルーティング名「contact.confirm」(入力値確認ページ)へ送信します。
@csrfディレクティブ
@csrfディレクティブ
はLaravelに標準装備されているセキュリティ対策用の構文です。Laravelでフォームを実装する時は基本必須になります。詳しくはこちらの記事をご覧ください。
oldヘルパ関数
value="{{ old('email') }}"
input要素のvalue属性には{{ old('title') }}
のようにoldヘルパ関数を使用します。oldヘルパ関数は直前のinput要素に入力された値を表示することができる関数です。引数には表示したいinput要素
のname属性
を指定します。これでバリデーションで弾かれた時や確認ページからこのページに戻った時でも値を保持することができます。
$errors変数
@if ($errors->has('name'))
<li>{{$errors->first('name')}}</li>
@endif
$errors変数
はIlluminate\Support\MessageBag
のインスタンスで何もしなくてもどこでも使えるように設定されています。バリデーションに弾かれた際にエラーを感知し$errors変数の中に自動でエラーメッセージが格納されます。
テンプレート側で表示する際は、hasメソッド
で中身の有無を確認し、firstメソッド
で1番目に格納されているエラーメッセージを取り出すのが基本です。firstメソッド
ではなくgetメソッド
にすると指定項目の全メッセージを、引数なしのallメソッド
にすると全項目の全エラーメッセージカードを取得できます。
oldヘルパ関数と$errors変数はフォーム実装にあたってよく使う手法なので覚えておくと便利です。
おすすめ記事:公式:バリデーション($errors変数)
入力確認ページ(confirm.blade.php)
入力された値で送信して良いかをユーザーに表示するページです。POSTされた値を受け取り表示させています。
重要なポイント
- コントローラ側から変数を受け取る
- 改行を制御するnl2br関数とe関数と!!構文
- 戻るボタンと送信ボタン
@section('content')
<form method="POST" action="{{ route('contact.send') }}">
@csrf
<label>メールアドレス</label>
{{ $inputs['email'] }}
<input name="email" value="{{ $inputs['email'] }}" type="hidden">
<label>タイトル</label>
{{ $inputs['title'] }}
<input name="title" value="{{ $inputs['title'] }}" type="hidden">
<label>お問い合わせ内容</label>
{!! nl2br(e($inputs['body'])) !!}
<input name="body" value="{{ $inputs['body'] }}" type="hidden">
<button type="submit" name="action" value="back">入力内容修正</button>
<button type="submit" name="action" value="submit">送信する</button>
</form>
@endsection
コントローラ側から変数を受け取る
{{ $inputs['email'] }}
ここで定義されている$inputs変数
はコントローラ側から受け取る予定の変数です。(変数名はなんでも可)後ほどコントローラ側で$inputs
の中にPOSTされた入力値を入れ込むことでindexページで入力された値を表示させています。
その下にtype=hidden(非表示)
にさせたinput要素
を作成します。value
には同じく$inputs
に格納されている入力値を渡します。問題がなければこのフォーム(indexではなくconfirm)の値を送信するためです。
改行を制御するnl2br関数とe関数と!!構文
{!! nl2br(e($inputs['body'])) !!}
入力値の本文部分を扱うためには改行コード(\n)を意識しないといけません。POST送信された値では改行部分は\n
で表されているので改行コードを変換してくれるnl2br関数
を使ってHTMLでの改行(<br>
)に変換します。
しかしLaravelではHTML特殊文字を自動エスケープ(内部的にhtmlspecialchars関数が実行)されてしまうので<>
部分がうまく表示されません。これを防ぐには自動エスケープを施さない{!! !!}文
を使います。
さらにこのままでは入力値に悪意あるコードを埋め込まれるリスクがあるので入力値のみに対してエスケープ処理を行います。Laravelでは長く冗長なhtmlspecialchars関数
を簡単に扱えるようにe関数
が用意されているのでこちらを使ってエスケープしておきます。
おすすめ記事:公式:エスケープ{!! !!}文
おすすめ記事:公式:e関数(htmlspecialchars関数)
戻るボタンと送信ボタン
<button type="submit" name="action" value="back">入力内容修正</button>
<button type="submit" name="action" value="submit">送信する</button>
続いて確認ページから、修正のために入力ページへ戻るボタンとお問い合わせを送る送信ボタンを設置します。ページの分岐はコントローラ側に託し、フォーム側では分岐できるようにPOST送信の値を別にしておきます(back/submit)。これでname=action
のinput要素
にはボタンが押された方の値が入るのでコントローラ側でチェックし分岐させることができるようになります。
メール送信時の本文部分(mail.blade.php)
問い合わせをしてくれたユーザーに対して受付完了したことを通知するメールの本文部分です。
お問い合わせ内容を受け付けました。<br>
<br>
■メールアドレス<br>
{!! $email !!}<br>
<br>
■タイトル<br>
{!! $title !!}<br>
<br>
■お問い合わせ内容<br>
{!! nl2br($body) !!}<br>
ここに記述した内容のメールがユーザーと自分が設定したメールアドレスに届くようにします。mail.blade.phpファイル内に記述するのは上記のみでOKです。HTMLで出力されるので改行は<br>
で実装します。
コントローラ側から変数の値は渡すのでひとまずフォーマットを作成して次に進みます。
送信完了ページ(thanks.blade.php)
送信完了したことをWebページで表示します。
@section('content')
{{ __('送信完了') }}
@endsection
ここまでで作り上げたのは処理のフレーム部分だけです。実際のデータの受け渡しや操作はコントローラ側で行います。
コントローラ側の処理
コントローラ処理を記述するために新しくコントローラを作成します。まだ作っていなければartisanコマンド
で生成しておきます。
php artisan make:controller ContactsController
コントローラ側で実装することは以下の5つです。
- ルーティング時のview設定
- POSTされたデータのバリデーション
- POSTされたデータのページ間受け渡し
- 修正ボタンと送信ボタンの実装
- 実際にメールを送る処理
ルーティング時に指定したアクションメソッド(index、confirm、send)をコントローラ内に記述していきます。
indexメソッド
まずは最初のお問い合わせ入力画面を表示できるようにindexメソッドを登録します。ここは単純にアクセスされたらページを表示するだけですので「contactフォルダ」の中のindex.blade.phpがreturnする様にしておきます。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ContactsController extends Controller
{
public function index()
{
// 入力ページを表示
return view('contact.index');
}
}
confirmメソッド
indexページ→確認ページに変異する際のアクションメソッドを定義します。
重要なポイント
- 入力値のバリデーション
- 入力値を変数に入れて確認ページに渡す
public function confirm(Request $request)
{
// バリデーションルールを定義
// 引っかかるとエラーを起こしてくれる
$request->validate([
'email' => 'required|email',
'title' => 'required',
'body' => 'required',
]);
// フォームからの入力値を全て取得
$inputs = $request->all();
// 確認ページに表示
// 入力値を引数に渡す
return view('contact.confirm', [
'inputs' => $inputs,
]);
}
入力値のバリデーション
$request->validate([
'email' => 'required|email',
'title' => 'required',
'body' => 'required',
]);
バリデーションとは「値が適切なものであるかチェックすること」です。LaravelではValidation機能も常設されています。使用するにはアクションメソッドの引数にrequestオブジェクト
を受け取ります。
// コントローラの上部に以下の文があることを確認
use Illuminate\Http\Request;
// アクションメソッドの引数にリクエストオブジェクトを渡す
public function confirm(Request $request) {
}
requestオブジェクトはHTTPリクエスト情報(POSTされた入力値やURL、リクエストヘッダ情報など)を取得できる機能です。その中にValidationメソッド
が用意されており、その引数にバリデーションしたい入力値の項目名(name属性
)とバリデーションルール(required
やemail
、max:255
など)を指定することができます。
おすすめ記事:公式:requestオブジェクト
バリデーションルールに違反する入力値が与えられた場合Validationメソッド
はエラーレスポンスを返し、直前のURL(ここでいうindexページ)へ自動でリダイレクトしてくれます。
その際にエラー内容が$errors変数
に格納されるのでindexページで作成した以下の部分でエラーを表示できるようになります。
@if ($errors->has('name'))
<li>{{$errors->first('name')}}</li>
@endif
各エラーメッセージのカスタマイズはresources/lang/en/validation.php
の中を変更すればOKです。
入力値を変数に入れて確認ページに渡す
バリデーションを通ったら次は入力値を変数に格納し、確認ページへ渡します。indexからPOST送信された値はrequestオブジェクト
のallメソッド
で項目名(name属性)がキー値、入力値が値の連想配列形式で取得できます。
// 連想配列形式で取得
$inputs = $request->all();
あとは一度変数に格納し、確認ページをviewする際に引数として渡せばOKです。渡す際のキー値をinputs
としているので確認ページ内では$inputs['項目名']
でアクセスできます。
最後のsendメソッドを記述する前に実際にメールを送信する機能を実装するため前準備をしておきます。
メール送信機能:Mailableクラス
Laravelではメール送信機能も簡単に実装できるMailableクラスが用意されています。使用方法は簡単でまずはプロジェクト内で以下のartisanコマンドを叩きます。
$ php artisan make:mail ContactsSendmail
これでapp/Mail/ContactsSendmail.php
が作成されます。
├── Laravelプロジェクト
│ ├── app
│ ├── Http
│ └── Mail
│ └── ★ContactsSendmail.php
作成された中身を以下のように変更します。
class ContactsSendmail extends Mailable
{
use Queueable, SerializesModels;
// プロパティを定義
private $email;
private $title;
private $body;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct( $inputs )
{
// コンストラクタでプロパティに値を格納
$this->email = $inputs['email'];
$this->title = $inputs['title'];
$this->body = $inputs['body'];
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
// メールの設定
return $this
->from('example@gmail.com')
->subject('自動送信メール')
->view('contact.mail')
->with([
'email' => $this->email,
'title' => $this->title,
'body' => $this->body,
]);
}
重要なポイント
- クラス内にプロパティを定義
- コンストラクタでプロパティに値を格納
- buildメソッド内でメール設定と作成
クラス内にプロパティを定義
まずは作成したMailableクラスの中にプロパティを定義します。メール作成に必要な以下の3つをprivateで定義します。
private $email;
private $title;
private $body;
コンストラクタでプロパティに値を格納
public function __construct( $inputs )
{
// コンストラクタでプロパティに値を格納
$this->email = $inputs['email'];
$this->title = $inputs['title'];
$this->body = $inputs['body'];
}
続いてクラス内のコンストラクタ(インスタンス時に実行されるメソッド)で変数$inputs
の中の値を各プロパティにセットします。変数$inputs
はインスタンス時の引数として渡す(コントローラ側から)のでここでは受取る前提で記述しておきます。
buildメソッド内でメール設定と作成
public function build()
{
// メールの設定
return $this
->from('example@gmail.com')
->subject('自動送信メール')
->view('contact.mail')
->with([
'email' => $this->email,
'title' => $this->title,
'body' => $this->body,
]);
}
Mailableクラスの中に元から定義されているbuildメソッド
がメール送信設定を司っている部分です。buildメソッド
の中で重要になってくるのがfromメソッド
とviewメソッド
です。
fromメソッドは引数にメールの差出人部分のメールアドレスを設定するメソッドです。自分のメールアドレスをここに設定します。viewメソッドはメールの本文部分を設定するメソッドです。引数には表示させたいbladeファイルを指定することでそのファイルの内容のメールを作成することができます。
subjectメソッド
はメールタイトルを設定するメソッドです。これでMailableクラスの準備は整いました。次はコントローラ側(sendメソッド)を実装していきます。
メールを送信する処理
コントローラのsendメソッドに実装したいのは以下のポイントです。
重要なポイント
- 入力値のバリデーション
- 戻るボタンと送信ボタンの分岐
- 実際にメールを送信する
- 二重送信対策
// 上部に追加
use App\Mail\ContactsSendmail;
public function send(Request $request)
{
// バリデーション
$request->validate([
'email' => 'required|email',
'title' => 'required',
'body' => 'required'
]);
// actionの値を取得
$action = $request->input('action');
// action以外のinputの値を取得
$inputs = $request->except('action');
//actionの値で分岐
if($action !== 'submit'){
// 戻るボタンの場合リダイレクト処理
return redirect()
->route('contact.index')
->withInput($inputs);
} else {
// 送信ボタンの場合、送信処理
// ユーザにメールを送信
\Mail::to($inputs['email'])->send(new ContactsSendmail($inputs));
// 自分にメールを送信
\Mail::to('自分のメールアドレス')->send(new ContactsSendmail($inputs));
// 二重送信対策のためトークンを再発行
$request->session()->regenerateToken();
// 送信完了ページのviewを表示
return view('contact.thanks');
}
}
入力値のバリデーション
まずはconfirmの時と同様にバリデーションしておきます。
戻るボタンと送信ボタンの分岐
// actionの値を取得
$action = $request->input('action');
続いて「戻るボタン」か「送信ボタン」どちらが押されたかを識別するためのaction
に格納されている値を取り出しておきます。メール作成用の変数として$inputs
も定義しておき、exceptメソッド
を使ってactionだけ除外した入力値を格納します。
// action以外のinputの値を取得
$inputs = $request->except('action');
実際にメールを送信する
あとは$action
に格納された値によって処理分岐します。back
であればviewにindexページを渡し画面をリダイレクトさせます。
submit(送信)
であればメールの送信処理、トークンのリセット、送信完了ページへの表示を行います。
メール送信機能をコントローラ内で使えるようにContactscontroller
の上部に以下の文を追記します。
use App\Mail\ContactsSendmail;
// ユーザにメールを送信
\Mail::to($inputs['email'])->send(new ContactsSendmail($inputs));
// 自分にメールを送信
\Mail::to('自分のメールアドレス')->send(new ContactsSendmail($inputs));
実際の送信処理はMailファザード
のsendメソッド
で行います。まずMailファザードのtoメソッド
の引数に送信先のメールアドレス(入力されたメールアドレス)を指定します。さらにsendメソッドを繋げてその引数にuse文で読み込んだMailableクラスのインスタンス化したものを渡します。Mailableクラスの引数には$inputs
(フォーム入力データの連想配列)を渡しておきます。
これでユーザー様へのお問い合わせ完了メールが送信できたので自分にもお問い合わせ内容メールを送るために->bcc(メールアドレス)
メソッド、もしくは上記のように再度をメール送信処理を記述すればOKです。
二重送信対策
- 送信ボタンを複数回押す
- ページを戻り再度送信ボタンを押す
- 完了ページでリロード
- CSRF攻撃
フォーム送信する場合上記が行われた時に同じPOST送信が複数回、送られてしまうことがあります。なので二重送信対策を施しておきます。
LaravelではCSRF対策としてワンタイムトークンを使用しています。簡単に言うと重複しないパスワードをPOST送信の際に乗せて送信側と受取側で照らし合わせマッチした場合のみ受け入れる方法です。先ほどの@csrfディレクティブがまさしくそうです。
この仕組みを利用して二重送信対策を行います。具体的にはPOST送信を一度受け入れたら、パスワードを採番しなおすことで同じPOSTデータ(古くなったパスワードを持ったPOST送信)を受け入れないようにする方法です。受け入れ側のパスワードはsession
の中に保持されていますのでパスワードを採番し直すには$requestオブジェクト
内のsession
に対してregenerateTokenメソッド
を実行するだけです。
$request->session()->regenerateToken();
これで送信処理と二重送信対策を終えたので最後に送信完了ページを表示させればOKです。
return view('contact.thanks');
.envファイルの変更
ここまででお問い合わせ機能のロジック部分が完成しました。最後にLaravelプロジェクト本体のメール設定を変更して終了です。メールの設定は隠しファイルである「Laravelプロジェクト」>「.env」の中に記述します。Macであれば「Shift + command + . 」の同時押しで表示/非表示を切り替えることができます。以下はGmailの場合の設定です。
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=自分のメールアドレス
MAIL_PASSWORD=アプリパスワード
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=自分のアドレス
MAIL_FROM_NAME="${APP_NAME}"
GmailをLaravelなどの外部のアプリで扱う場合はGmail側でも設定をしないといけません。Googleアカウントに管理者権限が付与されていれば「安全性の低いアプリを許可する」の設定をONにすればOKですが管理者権限がない場合はアプリパスワードを発行して使うのがオススメです。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。