WEBOPIXEL

Laravelでお問い合わせフォームを作ってみる

Posted: 2017.11.09 / Category: PHP / Tag: 

お問い合わせフォームを作るためにわざわざLaravel入れないかもしれないけど、Laravelが入ってたらお問い合わせフォームもLaravelで作りたいかもしれない。
ということでよくあるようなお問い合わせフォームをLaravelで作って見ます。

Sponsored Link

Laravel 5.4
laravelcollective/html 5.4
を使用します。

ルーティング

最初にルーティングの設定をします。
作成するのは「contact(入力画面)」「contact/confirm(確認画面)」「contact/complete(メール送信後画面)」です。

routes/web.php

Route::get('contact', 'ContactsController@index');
Route::post('contact/confirm', 'ContactsController@confirm');
Route::post('contact/complete', 'ContactsController@complete');

マイグレーション

マイグレーション(データベース)の設定です。
一般的なお問い合わせの項目だとテキストとかテキストエリアだけで済みそうですが、ラジオボタンとかチェックボックスも作ってみたいので、下記のような項目で作成します。

name text 名前
email email メールアドレス
gender radio 性別
type checkbox お問い合わせ種類
body textarea お問い合わせ内容

database/migrations/00000_create_contacts_table.php

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateContactsTable extends Migration
{
	public function up()
	{
		Schema::create('contacts', function (Blueprint $table) {
			$table->increments('id');
			$table->string('name');
			$table->string('email');
			$table->string('gender')->length(2);
			$table->string('type');
			$table->text('body');
			$table->timestamps();
		});
	}
	public function down()
	{
		Schema::dropIfExists('contacts');
	}
}

モデル

チェックボックスとかラジオボタンの項目はとりあえずモデルに記述してます。

app/Contact.php

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
	protected $fillable = [
		'type', 'name', 'email', 'gender', 'body'
	];
	static $types = [
		'商品について', 'サービスについて', 'その他'
	];
	static $genders = [
		'男', '女'
	];
}

index(入力画面)

ここからはアクション毎に見ていきます。
まずはフォームの入力画面。

モデルで作成した、ラジオボタンとチェックボックス用のデータをビューに渡します。

app/Http/Controllers/ContactsController.php

<?php
namespace App\Http\Controllers;

use App\Http\Requests\ContactRequest;
use App\Http\Controllers\Controller;
use App\Contact;

class ContactsController extends Controller
{
	public function index()
	{
		$types = Contact::$types;
		$genders = Contact::$genders;

		return view('contacts.index', compact('types', 'genders'));
	}
}

ちょっと長いですがビューです。
laravelcollectiveを使用すると、エラーで戻った時に表示するためのoldの記述は不要です。

resources/views/contacts/index.blade.php

@extends('layouts.app')

@section('content')


<div class="container">
	<div class="row">
		<div class="col-md-12">
			<div class="panel panel-default">
				<div class="panel-heading">お問い合わせ</div>
				<div class="panel-body">
					{{-- エラーの表示 --}}
					@if ($errors->any())
						<div class="alert alert-danger">
							<ul>
								@foreach ($errors->all() as $error)
									<li>{{ $error }}</li>
								@endforeach
							</ul>
						</div>
					@endif

					{!! Form::open(['url' => 'contact/confirm',
								'class' => 'form-horizontal']) !!}

					<div class="form-group{{ $errors->has('type') ? ' has-error' : '' }}">
						{!! Form::label('type', 'お問い合わせ種類:', ['class' => 'col-sm-2 control-label']) !!}
						<div class="col-sm-10">
							@foreach($types as $key => $value)
								<label class="checkbox-inline">
									{!! Form::checkbox('type[]', $value) !!}
									{{ $value }}
								</label>
							@endforeach
							@if ($errors->has('type'))
								<span class="help-block">
								<strong>{{ $errors->first('type') }}</strong>
							</span>
							@endif
						</div>
					</div>

					<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
						{!! Form::label('name', 'お名前:', ['class' => 'col-sm-2 control-label']) !!}
						<div class="col-sm-10">
							{!! Form::text('name', null, ['class' => 'form-control']) !!}

							@if ($errors->has('name'))
								<span class="help-block">
									<strong>{{ $errors->first('name') }}</strong>
								</span>
							@endif
						</div>
					</div>

					<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
						{!! Form::label('email', 'メールアドレス:', ['class' => 'col-sm-2 control-label']) !!}
						<div class="col-sm-10">
							{!! Form::email('email', null, ['class' => 'form-control']) !!}
							@if ($errors->has('email'))
								<span class="help-block">
									<strong>{{ $errors->first('email') }}</strong>
								</span>
							@endif
						</div>
					</div>

					<div class="form-group{{ $errors->has('gender') ? ' has-error' : '' }}">
						{!! Form::label('gender', '性別:', ['class' => 'col-sm-2 control-label']) !!}
						<div class="col-sm-10">
							@foreach($genders as $key => $value)
								<label class="checkbox-inline">
									{!! Form::radio('gender', $value) !!}
									{{ $value }}
								</label>
							@endforeach
							@if ($errors->has('gender'))
								<span class="help-block">
							<strong>{{ $errors->first('gender') }}</strong>
						</span>
							@endif
						</div>
					</div>

					<div class="form-group{{ $errors->has('body') ? ' has-error' : '' }}">
						{!! Form::label('body', '内容:', ['class' => 'col-sm-2 control-label']) !!}
						<div class="col-sm-10">
							{!! Form::textarea('body', null, ['class' => 'form-control']) !!}
							@if ($errors->has('body'))
								<span class="help-block">
									<strong>{{ $errors->first('body') }}</strong>
								</span>
							@endif
						</div>
					</div>

					<div class="form-group">
						<div class="col-sm-10 col-sm-offset-2">
							{!! Form::submit('確認', ['class' => 'btn btn-primary']) !!}
						</div>
					</div>

					{!! Form::close() !!}
				</div>
			</div>
		</div>
	</div>
</div>
@endsection

confirm(確認画面)

確認画面です。今回はFormRequestクラスを使用してバリデーションの設定を行います。

ラジオボタンやチェックボックスのように選択された項目が配列の中にあるか確認する場合はin:を使用します。

app/Http/Requests/ContactRequest.php

<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class ContactRequest extends FormRequest
{
	public function authorize()
	{
		return true;
	}
	public function rules()
	{
		return [
			'type' => 'required',
			'type.*' => 'in:商品について,サービスについて,その他',
			'name' => 'required|max:10',
			'email' => 'required|email',
			'gender' => 'required|in:男,女',
			'body' => 'required|max:1000'
		];
	}
	public function attributes() {
		return [
			'type' => 'お問い合わせ種類',
			'name' => 'お名前',
			'email' => 'メールアドレス',
			'gender' => '性別',
			'body' => '内容'
		];
	}
}

コントローラーにconfirmメソッドを追記します。
チェックボックスは配列で渡してるので、確認画面のビューに渡す前に「,」区切りの文字列にします。

app/Http/Controllers/ContactsController.php

public function confirm(ContactRequest $request)
{
	$contact = new Contact($request->all());

	// 「お問い合わせ種類(checkbox)」を配列から文字列に
	$type = '';
	if (isset($request->type)) {
		$type = implode(', ',$request->type);
	}

	return view('contacts.confirm', compact('contact', 'type'));
}

ビューでは入力された項目を表示する他にも、完了画面にPOSTで渡すので、フォームのhiddenでも設定しておきます。

resources/views/contacts/confirm.blade.php

@extends('layouts.app')
@section('content')

<div class="container">
	<div class="row">
		<div class="col-md-12">
			<div class="panel panel-default">
				<div class="panel-heading">お問い合わせ</div>
				<div class="panel-body">
					<p>誤りがないことを確認のうえ送信ボタンをクリックしてください。</p>

					<table class="table">
						<tr>
							<th>お問い合わせ種類</th>
							<td>{{ $type }}</td>
						</tr>
						<tr>
							<th>お名前</th>
							<td>{{ $contact->name }}</td>
						</tr>
						<tr>
							<th>メールアドレス</th>
							<td>{{ $contact->email }}</td>
						</tr>
						<tr>
							<th>性別</th>
							<td>{{ $contact->gender }}</td>
						</tr>
						<tr>
							<th>内容</th>
							<td>{{ $contact->body }}</td>
						</tr>
					</table>

					{!! Form::open(['url' => 'contact/complete',
									'class' => 'form-horizontal',
									'id' => 'post-input']) !!}

					@foreach($contact->getAttributes() as $key => $value)
						@if(isset($value))
							@if(is_array($value))
								@foreach($value as $subValue)
									<input name="{{ $key }}[]" type="hidden" value="{{ $subValue }}">
								@endforeach
							@else
								{!! Form::hidden($key, $value) !!}
							@endif

						@endif
					@endforeach

					{!! Form::submit('戻る', ['name' => 'action', 'class' => 'btn']) !!}
					{!! Form::submit('送信', ['name' => 'action', 'class' => 'btn btn-primary']) !!}
					{!! Form::close() !!}
				</div>
			</div>
		</div>
	</div>
</div>
@endsection

complete(データベースに保存・完了画面)

コントローラーにcompleteメソッドを追記します。
「戻る」ボタンがクリックされた場合はrequestの値を付与してindexに戻るという処理を最初にします。
あとはデータベースに保存するだけですね。

app/Http/Controllers/ContactsController.php

public function complete(ContactRequest $request)
{
	$input = $request->except('action');
	
	if ($request->action === '戻る') {
		return redirect()->action('ContactsController@index')->withInput($input);
	}

	// チェックボックス(配列)を「,」区切りの文字列に
	if (isset($request->type)) {
		$request->merge(['type' => implode(', ', $request->type)]);
	}

	// データを保存
	Contact::create($request->all());

	// 二重送信防止
    $request->session()->regenerateToken();
        
	return view('contacts.complete');
}

完了画面のビューはresources/views/contacts/complete.blade.phpに作成します。
基本的にはありがとうのようなメッセージを表示するだけだと思いますので省略します。

メール送信

お問い合わせなのでメールの送信も行いたいですね。
Mailableを継承したクラスを作成します。

app/Mail/Contact.php

<?php
namespace App\Mail;

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

class Contact extends Mailable
{
	use Queueable, SerializesModels;

	protected $content;
	protected $viewStr;

	public function __construct($content, $viewStr = 'to')
	{
		$this->content = $content;
		$this->viewStr = $viewStr;
	}

	public function build()
	{
		return $this->text('emails.'.$this->viewStr)
			->to($this->content['to'], $this->content['to_name'])
			->from($this->content['from'], $this->content['from_name'])
			->subject($this->content['subject'])
			->with([
				'content' => $this->content,
			]);
	}
}

今回は$this->textでビューを読み込んでるので、テキスト形式で送信されますが、$this->viewだとHTML形式だったり、$this->markdownだとマークダウンでHTMLが書けます。

ビューファイルにメールの文面の作成をします。

resources/views/emails/from.blade.php & to.blade.php

お名前:{{ $content['from_name'] }}
メールアドレス:{{ $content['from'] }}
性別:{{ $content['gender'] }}
お問い合わせ種類:{{ $content['type'] }}
お問い合わせ内容
{{ $content['body'] }}

後はコントローラーのcompleteに下記のように追記すればメールを遅れるようになります。

app/Http/Controllers/ContactsController.php

public function complete(ContactRequest $request)
{
	// ...

	// 送信メール
	\Mail::send(new \App\Mail\Contact([
		'to' => $request->email,
		'to_name' => $request->name,
		'from' => 'from@example.com',
		'from_name' => 'MySite',
		'subject' => 'お問い合わせありがとうございました。',
		'type' => $request->type,
		'gender' => $request->gender,
		'body' => $request->body
	]));

	// 受信メール
	\Mail::send(new \App\Mail\Contact([
		'to' => 'from@example.com',
		'to_name' => 'MySite',
		'from' => $request->email,
		'from_name' => $request->name,
		'subject' => 'サイトからのお問い合わせ',
		'type' => $request->type,
		'gender' => $request->gender,
		'body' => $request->body
	], 'from'));
}

開発環境でのログの確認

.envを下記のようにすれば、strage/logs/laravel.logに追記されます。

.env

MAIL_DRIVER=log

ここまでのものはGithubに置いてます。

参考サイト
『Laravel』を使ってカンタンなお問い合わせフォームを作ってみた | バックエンドへの道 [Laravel]Mailableで楽勝なメール処理 – atuweb : つながりを作るWebプログラマ