Laravel5でフォームを作る

Formから何らかの情報を取り入れてDBに格納、メール送信といったところが基本的な機能になるかと思います。Frontから攻める方法とDBから攻める方法が(私個人としては)あるのですが、コントローラーから攻める方法は他の機能とのからみができてからと思うので、ここではDBまわりから順番に作成してゆきます。

  1. 設計
  2. マイグレーションとモデル
  3. コントローラとビュー

Formの設計

入力項目に加えて、データを保存する形式を踏まえて設計します。とはいえLaravelではartisanコマンドである程度必要なものを揃えてくれます。
テーブル名は無難に「ContactUser」にしておきましょう。データの保存項目は以下のようにします。

カラム名 データ型 内容
id increments ID
last_name string (32) 姓*
first_name string (32) 名*
last_name_kana string (64) セイ
first_name_kana string (64) メイ
company string (128) 会社名 団体名
email string (email) Email*
category string (64) カテゴリー
message text 質問内容*
remark text 備考 (管理者記入)
deleted_at softDeletes(delFlag) 削除フラグ
created_at timestamps 作成日
updated_at timestamps 更新日

たいだいこんなものではないでしょうか。入力の必須項目を姓名、Email、質問内容にしておきましょう。

マイグレーション

テーブルの作成をします。手動で作成してもよいのですが、Laravelにはマイグレーションという機能があるのでそれを使った方がよいです。マイグレーションとモデルの作成の詳しい方法はこちら参照ください。
artisanコマンドでid,timestamps(created_at,updated_at)が自動的に作成されます。その他の項目は自分で追加してゆきます。

$ php artisan make:model ContactUser --migration
Model created successfully.
Created Migration: 2017_05_06_130517_create_contact_users_table

これでモデルとマイグレーションファイルが同時に生成されました。
modelは、app/ContactUser.php
migrationは、database/migrations/yyyy_mm_dd_hhiiss_create_contact_users_table.php
に作成されているはずです。migrationファイルは自動的に指定したテーブル名の複数形になっていることに留意。

テーブル作成のコマンド自体は以下のようになります。

$table->increments('id');
$table->string('last_name', 32);
$table->string('first_name', 32);
$table->string('last_name_kana', 64);
$table->string('first_name_kana', 64);
$table->string('company', 64);
$table->string('email');
$table->string('category', 64);
$table->text('message');
$table->text('remark');
$table->softDeletes('delFlag');
$table->timestamps();

このコードをマイグレーションファイルに追加したら、テーブルを作成します。

php artisan migrate

これだけで未作成のテーブルを作成してくれます。失敗した場合は簡単にロールバックできます。何度やっても大丈夫です。
もしこのときエラーが出てしまったときはDBの接続がうまくいっていない可能性があるので接続設定だけ見直してみましょう。
または指定のテーブルだけ作成したい場合は、

php artisan make:migration ContactUsers

で明示的にテーブル名を指定してもOKです。
これでテーブルがちゃんとできました。余談ですが、このテーブル作成はmigrationsというテーブルが自動的に作成され、その履歴が記録されている筈です。

また、経験的なものなんですが、Laravelのテーブル名は何かしらのprefixをつけておくとよいかと思います。というのは、モデルファイルがapp/*に作成されるのですが、app以下直下に置かれてしまうので、他のディレクトリ名と混じってバラバラになるのが嫌だからです。ここではContactUserですが、XxContactUserとかの方がよいかもしれません。
テーブル作成した際に日本語入力をする場合(って日本人何だから当然するだろって話ですが。。)コレーションの設定がデフォルトでは「collation’ => ‘utf8mb4_unicode_ci’」になっているかと思います。私的にはあんまり塩梅がよくないので、utf8_general_ciにしたいところです。この設定はconfig/database.phpであらかじめ設定しておきます。

// config/database.php
        'mysql' => [
            ....
            'charset' => 'utf8', // 'charset' => 'utf8mb4',
            'collation' => 'utf8_general_ci', // 'collation' => 'utf8mb4_unicode_ci',
            ....
        ],

ここまではOKでしょうか。

コントローラとビューの作成

FormからPOSTできるようにコントローラとビューを作成しておきます。
アクセスURIは

/contactus/index
/contactus/confirm
/contactus/complete

ぐらいでよいかと思います。
実際いろんな作り方があるのですが、確認画面は特に要らないという場合もあるでしょう。大きなアクションの区分けとしてはPOSTを受け取る部分と保存する部分に分かれていれば事足りるからです。とはいえ、ここではわかりやすくURIは3つに分けてみました。
これらに対応するルーティングとコントローラメソッド、ビューを作成しましょう。

ルーティング

// routes/web.php
....
// contact us
Route::get('/contactus/index', 'ContactsController@index');
Route::match(['get', 'post'],'/contactus/confirm', 'ContactsController@confirm');
Route::get('/contactus/complete', 'ContactsController@complete');
....

ちょっと説明すると、indexはただのFormパーツ表示、comfirmでPOSTを受けて、completeでは内部的に受け取った値を保存やメール送信で処理するというかたちになります。ルーティングとメソッドが1対1で対応しているのでわかりやすいです。

コントローラの作成

コントローラは雛形とartsanで作成します。

php artisan make:controller ContactsController

ここで注意点は、コントローラ名は複数形でってことです。単数形だとコマンドで作成できません。
また作成したコントローラは空っぽなので、まだ何もありません。ここに対応するメソッドを追加してゆきます。

<?php
// app/Http/Controllers/ContactsController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ContactsController extends Controller
{
    //
}

テンプレートをまだ作成していないので、ここではreturnで文字列だけ返してあげるようにして、ルーティングとコントローラが接続できればよいと思います。

<?php
// app/Http/Controllers/ContactsController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ContactsController extends Controller
{
    //
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    public function index()
    {
        return 'index';
    }

        public function confirm()
    {
        return 'confirm';
    }

        public function complete()
    {
        return 'complete';
    }
}

テンプレートの作成

ご存知の通りLaravelのデフォルトのテンプレートはbladeです。かっこいいですね。
ここでは、viewのテンプレートを、

resources/views/contact/index.blade.php
resources/views/contact/confirm.blade.php
resources/views/contact/complete.blade.php

で作成しましょう。
indexではFormパーツ、confirmではPOSTの送信値の表示、completeでは内部処理を行った後の完了画面といったデザインになります。

index.blade.php

Formパーツの作成ではいつものように書けばよいのですが、LaravelはCSRFトークンがデフォルトで備わっているので大変便利です。hiddenにその内容を追加すればOKです。POSTされたときに内部的に自動でCSRFトークンが有効かどうかを判別してくれます。
(ここではトークンがエラーになった場合の処理は割愛します。)

<form action="/contactus/confirm" id="" name="" class="" method="post">
    <div>
        <label for="last_name">姓</label><input type="text" id="last_name" name="last_name">
        <label for="first_name">名</label><input type="text" id="first_name" name="first_name">
    </div>
    <div>
        <label for="last_name_kana">セイ</label><input type="text" id="last_name_kana" name="last_name_kana">
        <label for="first_name_kana">メイ</label><input type="text" id="first_name_kana" name="first_name_kana">
    </div>
    <div>
        <label for="company">会社名</label><input type="text" id="company" name="company">
    </div>
    <div>
        <label for="email">Email</label><input type="text" id="email" name="email">
    </div>
    <div>
        <label for="chk1">ご意見・ご要望</label><input type="checkbox" name="chk1" id="chk1" value="ご意見・ご要望" checked>
        <label for="chk2">質問・お問い合わせ</label><input type="checkbox" name="chk2" id="chk2" value="質問・お問い合わせ">
        <label for="chk3">その他</label><input type="checkbox" name="chk3" id="chk3" value="その他">
    </div>
    <div>
        <label for="message"></label>
        <textarea id="message" name="message"></textarea>
    </div>
    <div>
        <input type="hidden" name="_token" value="{{csrf_token()}}">
        <input type="submit" id="submit" name="submit" value="送信">
    </div>
</form>

さてここでビューのindex.blade.phpとコントローラを結び付けなくてはなりません。
先程は文字列だけ返していたコントローラのリターン値に、それぞれテンプレートのパスを設定してあげます。これで/contactus/index,/contactus/confirm,/contactus/completeにアクセスするとページを表示できるようになります。

return view('contact/index');
return view('contact/confirm');
return view('contact/complete');

confirmでポストを受け取る

Formから送信されたPOST値を受け取るには、いろいろな方法がありますがここでは、Dependency Injection (DI)をつかってリクエスを処理してみます。
コントローラに、Illuminate\Http\Requestを追加してconfirmメソッドの引数でリクエストを受けられるようにしておきます。
※Illuminate\Support\Facades\DBも後々使うので追加しておいてかまいません。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request; //追加
use Illuminate\Support\Facades\DB; //追加

class ContactsController extends Controller
{

    public function index()
    {

        return view('contact/index');
    }

        public function confirm(Request $request) //Request $request追加
    {
        var_dump($request->all()); //postを表示
        return view('contact/confirm');
    }

        public function complete()
    {
        return view('contact/complete');
    }
}

$request->all()はあんまり実践的ではないので、個別に受け取るには以下のようにnameの値を引数にとってあげるとOKです。

$request->input('last_name');
$request->input('first_name');
$request->input('last_name_kana');
$request->input('first_name_kana');
$request->input('company');
$request->input('email');
$request->input('chk1');
$request->input('chk2');
$request->input('chk3');
$request->input('message');
$request->input('_token');
$request->input('submit'));

POST値をうけとったところでの処理として正しい値が入ってきたかどうかというバリデーションの処理が必要です。Laravelではバリデーションの処理を半自動的に行ってくれる機能があります。App\Http\Controllers\Controllerクラスの中にはValidatesRequestsトレイトが常に呼び出されているのでコントローラ内でいつでも使えるといった具合になってます。
Laravel 5.4 バリデーション
ここまで多機能だとほんとコーディングが楽ですね。

public function confirm(Request $request)
{
// do to validate
$this->validate($request, [
    'last_name' => 'required|max:16',
    'first_name' => 'required|max:16',
    'email' => 'required|email',
    'message' => 'required|max:1200',
]);
....
// 確認画面の処理

バリデーションに使える引数はLaravelのバリデーションで指定できる内容をざっくりまとめ直しました。にざっくりじゃなくて結構詳しく載っていますのでご参照ください。
ただここはクソ面倒な作業が必要なのと、比較的設計思想みたいなものが色濃く出るところなので、バリデーションについては割愛します。ここでは単に必須項目とmaxlength的なものの設定をして、これを通らなかった場合は単にポストできないというところでいったん終わらせます。
(おそらく完璧なUIを望む場合はAjaxでもってエラーをjsonで返す形式がベストかと思います。)
Laravel5のフォームバリデーション
ここまででPOSTの受取ができました。

POST値をセッションに格納しておく

確認画面から完了画面への遷移では特にPOSTする必要がないのでセッションに値を保持しておきます。
セッションはグローバルセッションヘルパを利用するかリクエストのインスタンスを経由して操作する方法とあるようですが、正直どっちでもいいのでここではリクエストを経由してセッション操作します。
Laravel 5.4 HTTPセッション

$request->session()->put('last_name', $request->input('last_name'));
$request->session()->put('first_name', $request->input('first_name'));
$request->session()->put('last_name_kana', $request->input('last_name_kana'));
$request->session()->put('first_name_kana', $request->input('first_name_kana'));
$request->session()->put('company', $request->input('company'));
$request->session()->put('email', $request->input('email'));
$request->session()->put('email', $request->input('chk1'));
$request->session()->put('email', $request->input('chk2'));
$request->session()->put('chk3', $request->input('chk3'));
$request->session()->put('message', $request->input('message'));
$request->session()->put('_token', $request->input('_token'));
$request->session()->put('submit', $request->input('submit'));

ここまできたらconfirmのテンプレートに値を送ってあげればOKです。
compact関数かwithメソッドというところになりますが、個人的にはwith()で送ってあげる方が好きです。

return view('contact/confirm')
->with( [
    'last_name'=> $request->session()->get('last_name'),
    'first_name'=> $request->session()->get('first_name'),
    'last_name_kana'=> $request->session()->get('last_name_kana'),
    'first_name_kana'=> $request->session()->get('first_name_kana'),
    'company'=> $request->session()->get('company'),
    'email'=> $request->session()->get('email'),
    'chk1'=> $request->session()->get('chk1'),
    'chk2'=> $request->session()->get('chk2'),
    'chk3'=> $request->session()->get('chk3'),
    'message'=> $request->session()->get('message'),
]);

withメソッドで複数の値を渡す際は連想配列にしてあげて渡します。
bladeの方では、keyを例のblade独特の変数にしてあげると取得できます。また完了画面への処理はFormパーツではなく単にアンカーリンクで事足りるのでリンクにしておきます。

{{$last_name}}<br>
{{$first_name}}<br>
{{$last_name_kana}}<br>
{{$first_name_kana}}<br>
{{$company}}<br>
{{$email}}<br>
{{$chk1}}<br>
{{$chk2}}<br>
{{$chk3}}<br>
{{$message}}<br>
<br>
<a href="/contactus/complete">送信</a>

DB格納とメール送信

最後にDB格納とメール送信するわけですが、先程セッションに保存した値を完了画面で取り出してみます。ここで値を取り出せたらOKです。
また先程の説明の通りリクエストインスタンスを経由してセッションデータを取得しているので、completeメソッドの引数にリクエストを与えてあげます。
ここまでできたら後はDBに保存してメール送信となります。

sessionからデータを取り出す

メソッド自体はこんな塩梅になるかと思います。

public function complete(Request $request)
{
    // Session
    var_dump($request->session()->get('last_name'));
    var_dump($request->session()->get('first_name'));
    var_dump($request->session()->get('last_name_kana'));
    var_dump($request->session()->get('first_name_kana'));
    var_dump($request->session()->get('company'));
    var_dump($request->session()->get('email'));
    var_dump($request->session()->get('chk1'));
    var_dump($request->session()->get('chk2'));
    var_dump($request->session()->get('chk3'));
    var_dump($request->session()->get('message'));
    var_dump($request->session()->get('_token'));
    var_dump($request->session()->get('submit'));

    return view('contact/complete');
}

テーブルにインサート

DBに格納するには、モデルとコントローラの関連付けとEloquentの利用で行います。簡易的にクエリビルダーでやるのもいいんですが、せっかくなのでモデルを通してデータをあれこれしてみます。
DBを扱うためには先述したように、Illuminate\Support\Facades\DBをuseします。それからモデル自体を呼び出しましょう。

use Illuminate\Support\Facades\DB;
use App\ContactUser;

これで準備完了です。
あとはモデルが取得したオブジェクトに更新情報をあたえてsave()するだけになります。
Laravel 5.4 Eloquent:利用の開始

$contact = new ContactUser;
var_dump($contact);
$contact->last_name = $request->session()->get('last_name');
$contact->first_name = $request->session()->get('first_name');
var_dump($contact->last_name);
$contact->save();

とはいえ、私もこんなことが時折起こりますのでご注意。
Laravelでマイグレーション後にモデル作成してもデータが登録ができない

諸々細かい処理

Form送信は諸々細かい処理をもっといっぱい入れていかないといけません。
/contactus/completeに直接アクセスしたときの処理とか、いろいろです。ここから先はいろいろ面倒ですがおのおの頑張ってください。

Last update: 2017.05.28 (日)