九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、ウェブ開発をしているとよく必要になる機能のひとつにお問い合わせフォームがあります。
もちろんクライアントさんだけが利用するタイプのサイトなら不要ですが、それ以外はだいたいどのようなサイトでもユーザーが運営者に連絡ができるようお問い合わせフォームを開設しています。
そこで、今回は比較的ご要望が多いお問い合わせフォームを Larave
+ Vue
で作ってみます。
いつもの内容より基本的な内容が多くなっているので、ぜひ参考にしてくださいね。(最後に今回のプログラムコード一式をダウンロードできます!)
開発環境: Laravel 5.7
やりたいこと
今回開発する内容は以下のようなお問い合わせフォームです。
- 問い合わせ内容をメールで送信
- バリデーション(入力チェック)をする
- Ajax通信で送信(ページの移動はなし)
では実際に開発していきましょう!
お問い合わせの入力ページをつくる(デザイン部分)
ルーティングをつくる
今回必要なルーティングは以下の2つです。
- お問い合わせの入力ページ
- Ajaxでお問い合わせ内容を受信するページ
そのため、以下のような内容を追加しましょう。
Route::get('contact', 'ContactController@input'); // 入力ページ Route::post('contact', 'ContactController@send'); // 送信ページ(Ajax)
※ 本来はHTTPとAjaxなのでコントローラーは分ける方がいいですが、今回は説明のためシンプルにコントローラーはひとつだけで作成しています。
コントローラーをつくる
では、次にコントローラーです。
以下のコマンドで作成しましょう。
php artisan make:controller ContactController
これでapp/Http/Controllers/ContactController.php
が作成されました。
なお、今の段階では、入力ページのためにinput()
だけを作成しておきましょう。
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class ContactController extends Controller { public function input() { return view('contact.input'); } }
ビューを作成する
続いては、実際にユーザーが目にするビューです。
resources/views/contact/input.blade.php
を作成します。
※ なお、今回はいつものbootstrap
ではなくZurb Foundationを使ってみます。こちらのデザインを使わせてもらいました。m_ _m
<html> <head> <link href="//cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css" rel="stylesheet" id="bootstrap-css"> <style> .error_txt{margin-bottom: 10px; display: block; color: #f00; font-size: 13px;} .foundation-example__ex-wrapper{ background-color: lightblue; } .foundation-example__in-wrapper{ margin: auto; max-width: 960px; background: white; padding: 30px; } .foundation-example__title{ font-size: 40px; margin-bottom: 25px; } </style> </head> <body> <section> <div style="height: 50px;background: lightblue;"></div> <div class="foundation-example__ex-wrapper"> <div class="foundation-example__in-wrapper"> <div class="foundation-example__title"> お問い合わせ</div> <form> <div class="row"> <div class="large-6 columns"> <label>名 <input type="text"> <div class="error_txt"></div> </label> <label>メールアドレス <input type="text"> <div class="error_txt"></div> </label> </div> <div class="large-6 columns"> <label>姓 <input type="text"> <div class="error_txt"></div> </label> <label>ご用件 <select></select> <div class="error_txt></div> </label> </div> </div> <div class="row"> <div class="large-12 columns"> <label>お問い合わせ内容 <textarea rows="10" placeholder="お問い合わせの内容をご入力ください。"></textarea> <div class="error_txt></div> </label> </div> </div> <div class="row"> <div class="large-12 columns text-center"> <button class="button expanded" type="button">送信する</button> </div> </div> </form> </div> </div> <div style="height: 50px;background: lightblue;"></div> </section> </body> </html>
では、ページを開いて確認してみましょう。
では、次の項目からこのフォームで問い合わせフォームを作っていきましょう。(Foundation
もきれいなデザインになりますね! ^^)
お問い合わせの入力ページをつくる(JavaScript部分)
Vueを使えるようにする
まず、Vueを使えるようにcdnでファイルを読み込んで基本形をつくります。
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js"></script> <script> new Vue({ el: '#app' }); </script>
そして、Vueがアクセスするエリアにid
を追加。
<section id="app"> <!-- 省略 --> </section>
データ変数をつくってバインディングする
まずは、data
に次のような変数を追加します。
new Vue({ el: '#app', data: { firstName: '', lastName: '', email: '', subjectId: '', body: '', subjects: { 100: '商品に関するご質問', 200: 'お支払に関するご質問', 300: 'ショッピングに関するご質問', 400: 'ポイントに関するご質問', 500: 'ご質問・その他', } } });
そして、各<input>
、<select>
、<textarea>
タグにv-model
を使ってデータ・バインディングします。
<label>姓 <input type="text" v-model="lastName"> <!--省略--> <label>メールアドレス <input type="text" v-model="email"> <!--省略--> <label>名 <input type="text" v-model="firstName"> <!--省略--> <label>ご用件 <select v-model="subjectId"></select> <!--省略--> <label>お問い合わせ内容 <textarea rows="10" placeholder="お問い合わせの内容をご入力ください。" v-model="body"></textarea>
これで、入力内容変更になると同時にVueの変数も自動的に更新されます。(これが、データ・バインディング)
そして、「ご用件」のセレクトボックスですが、まだ選択肢が無いのでこれもVueで準備しておきましょう。
<select v-model="subjectId"> <option value=""></option> <option v-for="(subject,id) in subjects" :value="id" v-text="subject"></option> </select>
未選択の場合は空白にしておきたいので、1行目の<option>
はvalueが空にしています。
そして、2行目はv-for
を使って先ほどdata
に書いたsubjects
をループし、value
と表示内容をセットしています。
これをVueに表示させると次のようになります。
<select> <option value=""></option> <option value="100">商品に関するご質問</option> <option value="200">お支払に関するご質問</option> <option value="300">ショッピングに関するご質問</option> <option value="400">ポイントに関するご質問</option> <option value="500">ご質問・その他</option> </select>
では実際にうまく表示されているか確認しておきましょう。
うまくいきました!
Ajaxで送信する部分をつくる
では、送信ボタンをクリックした後のプログラムをつくっていきましょう。(IE11のことも考えてES6は使わないでコーディングしています ^^;)
まず、Ajax通信ができるようにaxios
というJSライブラリをcdn
から読み込みます。
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
次に送信ボタンにクリックイベントを追加し、onSubmit()
を実行できるようにします。
<text class="button" type="text" @click="onSubmit()">送信する</text>
onSubmit()
の中身はこうなります。
methods: { onSubmit: function() { if(!confirm('送信します。よろしいですか?')) { return; } var params = { first_name: this.firstName, last_name: this.lastName, email: this.email, subject_id: this.subjectId, body: this.body }; axios.post('/contact', params) .then(function(response){ // 成功した時 }) .catch(function(error){ // 失敗したとき }); } }
バリデーション部分をつくる
後で必要になるので、Ajaxで受信をする部分をつくる前にバリデーション(入力チェック)を作っておきましょう。次のコマンドで専用のRequest
を作成します。
php artisan make:request ContactRequest
そして、ここに入力チェック・ルールを書いていきましょう。(太字の部分が変更した場所です)
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class ContactRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'first_name' => 'required', 'last_name' => 'required', 'email' => 'required|email', 'subject_id' => 'required|in:100,200,300,400,500', 'body' => 'required' ]; } }
※ 本来はsubject_id
のin
は一元管理したデータから呼び出すべき(Vue部分も)ですが、今回は分かりやすい説明のために直書きしています。
ただし、このままではエラー内容は英語のみになってしまうので、resources/lang/ja/validation.php
を作成して、Laravel-lang(バリデーション翻訳)の内容をコピーしましょう。
そして、さらに一番下のattributes
に入力値の名前を追加します。
'attributes' => [ 'first_name' => '名', 'last_name' => '姓', 'email' => 'メールアドレス', 'subject_id' => 'ご用件', 'body' => 'お問い合わせ内容' ],
メール部分をつくる
LaravelにはMailable
というクラスが用意されていて、これを利用すると簡単にメール送信ができるようになっています。
では、次のコマンドでContacted
という名前のMailable
クラスをつくりましょう。
php artisan make:mail Contacted
作成されたapp/Mail/Contacted.php
を開いて、中身を変更します。
<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Queue\ShouldQueue; class Contacted extends Mailable { use Queueable, SerializesModels; private $_params = []; /** * Create a new message instance. * * @return void */ public function __construct($params) { $this->_params = $params; } /** * Build the message. * * @return $this */ public function build() { return $this->subject('お問い合わせがありました') ->with('params', $this->_params) ->view('mail.contact'); } }
さらに、まだmail.contact
というビューがないので、resources/views/mail/contact.blade.php
を作成し以下の内容を追加します。
以下のお問い合わせを受け付けました。<br> <br><br> 名前: {{ $params['last_name'] }} {{ $params['first_name'] }}<br> メールアドレス: {{ $params['email'] }}<br> ご用件: {{ $params['subject'] }}<br> お問い合わせ内容: <pre>{{ $params['body'] }}</pre>
Ajaxで受信する部分をつくる
続いて、送信されたデータを受信する部分をつくっていきましょう。
ここからは、Laravel(PHP)部分になります。
先ほど作ったContactController
を開いてsend()
メソッドを追加します。
<?php namespace App\Http\Controllers; use App\Http\Requests\ContactRequest; use Illuminate\Http\Request; class ContactController extends Controller { // 省略 public function send(ContactRequest $request) { // ここでメール送信 } }
ここで、引数の中にContactRequest
を使っています。こうすることで自動的に先ほど作ったバリデーションを実行し、問題があればエラーを返してくれます。
では、テストで何も入力せずにデータを送信して、レスポンスがどうなるかをチェックしておきましょう。
うまくいきました。
エラー内容も日本語化されています。
メール送信部分をつくる
では、実際にメールを送信する部分です。
同じくContactController
を開いてsend()
メソッドに以下の内容を追加します。
public function send(ContactRequest $request) { $subjects = [ '100' => '商品に関するご質問', '200' => 'お支払に関するご質問', '300' => 'ショッピングに関するご質問', '400' => 'ポイントに関するご質問', '500' => 'ご質問・その他', ]; $params = [ 'first_name' => $request->first_name, 'last_name' => $request->last_name, 'email' => $request->email, 'subject_id' => $request->subject_id, 'body' => $request->body, 'subject' => $subjects[$request->subject_id] ]; \Mail::to('admin@example.com')->send(new Contacted($params)); }
※ ここでも説明を分かりやすくするために$subjects
は直書きしていますが、どこかで共通データにすべきです。
テスト送信する
では、今の時点で以下のような内容を入力し、メール送信できるかをチェックしておきましょう。
うまくいけば、以下のようなメッセージが送信されます。
Ajax送信が失敗した時の部分をつくる
では最後に、入力エラーが発生したときの表示も実装しておきましょう。
まず、data
に各入力エラーが入るerrors
という変数をつくります。
data: { // 省略 errors: {} },
そして、Ajax送信の直前に必ず初期化し、エラーの場合のみそれぞれのエラー内容を格納します。
this.errors = {}; // 省略 axios.post('/contact', params) .then(function(){ // 省略 }) .catch(function(error){ var errors = {}; for(var key in error.response.data.errors) { errors[key] = error.response.data.errors[key].join('<br>'); } self.errors = errors; });
※ なお、一旦ローカル変数のerrors
に全エラーを格納しているのは、Vueは配列の中身が変更されてもコンテンツの自動変更は行われないからです。そのため、最後に一気にデータを変更しています。
では、このエラーをv-html
を使ってそれぞれの位置で表示できるようにしましょう。
<div class="error_txt" v-html="errors.last_name"></div> // 省略 <div class="error_txt" v-html="errors.email"></div> // 省略 <div class="error_txt" v-html="errors.first_name"></div> // 省略 <div class="error_txt" v-html="errors.subject_id"></div> // 省略 <div class="error_txt" v-html="errors.body"></div>
※ <br>
タグが入ってくる可能性があるのであえてv-html
にしています。
これで以下のようにエラー内容が表示されるようになりました。
Ajax送信が成功した時の部分をつくる
では、メッセージの送信が完了した場合に入力フォームを消し、「メッセージが送信されました。ありがとうございました。」と表示するようにしてみましょう。
まず、data
にメールが送信されたかどうかが分かるemailSent
を追加します。
data: { // 省略 emailSent: false },
そして、メール送信が完了した時点でemailSend
をtrue
に切り替えます。
var self = this; // 省略 axios.post('/contact', params) .then(function(){ self.emailSent = true; }) .catch(function(error){ // 省略 });
※ 関数内はthis
が違う場所を指してしまうので、self
に格納していることに注意してください。(ES6のアロー関数が使えば無視できるのですが、IE対応しているのでこうなりました ^^;)
そして、Vueのv-if
で表示の切り替えです。
まず<form>
タグをメールが送信されていない場合だけ表示するようにします。
<form v-if="!emailSent">
逆に、メールが送信された場合には完了メッセージを表示するようにします。(間に何も入れる予定がないならv-else
でもいいでしょう)
<!-- 省略 --> </form> <div v-if="emailSent">メッセージが送信されました。ありがとうございました。</div>
これで送信が完了したら以下のようになります。
おまけ
途中で何度か書きましたが、やはり以下のデータはどこかで一元管理すべきなので、以Laravelのモデルから取得できるようにしてみましょう。
subjects: { 100: '商品に関するご質問', 200: 'お支払に関するご質問', 300: 'ショッピングに関するご質問', 400: 'ポイントに関するご質問', 500: 'ご質問・その他', },
まず、次のコマンドでEmailSubject
というモデルを作ります。
php artisan make:model EmailSubject
そして、中身を次のように変更します。(今回はDBへ格納はしないのでextends
部分は削除しました)
<?php namespace App; class EmailSubject { public static function all() { return collect([ '100' => '商品に関するご質問', '200' => 'お支払に関するご質問', '300' => 'ショッピングに関するご質問', '400' => 'ポイントに関するご質問', '500' => 'ご質問・その他', ]); } }
これで、\App\Email::all()
とするとLaravelのコレクションで全ご用件データを取得することができます。
では、先ほどのinput.balde.php
の中でこのモデルを使ってみましょう。
data: { firstName: '', lastName: '', email: '', subjectId: '', body: '', subjects: {!! \App\EmailSubject::all() !!}, emailSent: false, errors: {} },
※ Laravelのコレクションはそのものを表示しようとすると自動的にJSON化されます。
また、ContactController
の$subjects
も置き換えます。
$subjects = \App\EmailSubject::all();
最後に、ContactRequest
のバリデーション・ルールも変更すればご用件の一元管理は完了です。
'subject_id' => 'required|in:'. \App\EmailSubject::all()->keys()->implode(','),
今回のプログラム・コードをダウンロード
今回の開発で実際に作成したプログラム・コード一式を以下からダウンロードできます。ぜひ学習に役立ててください。
※ なお、validation.php
はご自身でコピーして使用してください。