【Laravel6】同時に2つのテーブルにデータを保存して、表示する方法

CODE

この記事は、PHPのフレームワーク「Laravel」で同時に2つのテーブルにデータを保存して、表示する方法の解説記事です。

お問い合わせフォームを作成時に、同時に2つのテーブルにデータを保存するという実装をしたので、シェアします。

私(わや@wayasblog)自身、Laravelを最近勉強し始めたので、かなり丁寧に解説した初心者向けの記事となっています。

コードだけ見たい!という方は、こちら(GitHub)からどうぞ。

» Laravelで作成したアプリやアウトプットの一覧はこちら

スポンサーリンク

「同時に2つのテーブルにデータを保存する」のイメージ

スクリーンショットと図解で紹介していきます。

お問い合わせページ

名前とチェックボックスだけなので、かなりシンプルなお問い合わせフォームです。

そして、名前とチェックボックスは別テーブルに格納されるようになっています。

テーブルの設計は以下の通りです。

inquirersテーブルのidと、checksテーブルのinquirers_idが紐付いています。

お問い合わせをする度に、inquirersテーブルは1行、checksテーブルにはチェックボックスを選択した分の行数が入るようになっています。

お問い合わせページのバリデーション

お問い合わせ完了ページ

お問い合わせ一覧の表示ページ

一覧表示ページでは、2つのテーブルに別々保存したものを、一緒に表示します。

誰がどの選択肢を選んだかがわかりますね。

初期設定

インストールやお決まりの初期設定をしていきます。

Laravelのインストール

composer create-project --prefer-dist laravel/laravel inquiry "6.*"

phpMyAdimnでDB作成

今回は「inquiry」というDBを作成しました。

.envファイルの設定

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=inquiry
DB_USERNAME=root
DB_PASSWORD=root

configファイルの設定

config/app.phpで日本語と時間の設定をします。

'timezone' => 'Asia/Tokyo',
'locale' => 'ja',

以上で、初期設定は終わりです。

コントローラーの作成

コントローラーの役割は、以下の通りです。

  • ルートから受け取った情報をモデルに処理をお願いする
  • モデルから受け取った情報をビューに表示する

具体的には、DBとの接続と処理、バリデーション等を書いていきます。

php artisan make:controller InquiriesController

※Laravelの規約に従うため、コントローラー名は必ず複数形にします。

app/Http/Controllers/InquiriesController.phpが作成されました。

ここに、以下のfunctionを作っていきます。

  • index
  • process
  • complete

※processは、DB挿入のためのものでページは作成しないので、viewは渡しません。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class InquiriesController extends Controller
{
    public function index()
    {
        return view('inquiries.index');
    }

    public function process()
    {

    }

    public function complete()
    {
        return view('inquiries.complete');
    }
}

ルーティングの設定

routes/web.phpを編集します。

// お問い合わせ入力ページ
Route::get('/', 'InquiriesController@index')->name('inquiry');

// DB挿入
Route::post('/process', 'InquiriesController@process')->name('process');

// 完了ページ
Route::get('/complete', 'InquiriesController@complete')->name('complete');

ビューの作成

まず、resources/viewsにinquiriesフォルダを作成し、

  • index
  • complete

以上の2つのbladeテンプレート作成します。

フォームは「Laravel Collective」の「Forms & HTML」を使うので、インストールします。
※参考:Laravel Collective

composer require laravelcollective/html

チェックボックスの内容を定数にする

ビューを作成する前に、チェックボックスの内容を定数にして管理しやすくします。

定数で管理する理由は、以下の通りです。

  • 他のページでチェックボックスを使用する場合、再度すべて書く必要がある
  • チェックボックスの内容を修正したい場合、全ページすべてを直すのが手間
  • チェックボックスの数が増えたら、管理が大変

そういう訳で、チェックボックスを定数で管理をしていきます。

configフォルダにconst.phpファイルを作成します。

<?php

return [
    'check_item' => [
        1 => '選択肢1',
        2 => '選択肢2',
        3 => '選択肢3',
        4 => '選択肢4',
        5 => '選択肢5',
    ],
];

選択肢が増えたら、ここに記述すればOKですね。

次に、app/Http/Controllers/InquiriesController.phpで定数を使用するための記述をします。

ひとまず、以下のような感じになりました。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class InquiriesController extends Controller
{
    public function index()
    {
        $check_items = config('const.check_item');

        return view('inquiries.index', [
            'check_items' => $check_items,
        ]);
    }

    public function process()
    {

    }

    public function complete()
    {
        return view('inquiries.complete');
    }
}

index.blade.php

チェックボックスの内容はforeachで回して表示しています。

<!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 mt-5">
        <h1 class="text-center mb-5">お問い合わせ</h1>
        <div class="container">
            {!! Form::open(['route' => 'process', 'method' => 'POST']) !!}
                {{ csrf_field() }}
                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">お名前</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">選択(複数選択可)</p>
                    <div class="col-sm-8">
                        @foreach ($check_items as $key => $check_item)
                            <label>{{ Form::checkbox('check_item[]', $key) }}{{ $check_item }}</label>
                        @endforeach
                    </div>
                </div>
                
                <div class="text-center">
                    {{ Form::submit('送信', ['name' => 'submit', 'class' => 'btn btn-primary']) }}
                </div>
            {!! Form::close() !!}
        </div>
    </div>
</body>
</html>

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="container text-center mt-5">
        <h1 class="mb-5">お問い合わせありがとうございました!</h1>
        <a href="{{ route('inquiry') }}">お問い合わせページに戻る</a>
    </div>
</body>
</html>

モデルとマイグレーションの作成

モデルはDBとの連携を行う機能、マイグレーションはDBを管理する機能です。

DBのテーブルの設計は、以下の通りでした。

実際にモデルとマイグレーションを作成していきます。

php artisan make:model Inquirer -m

まずは、左側のinquirersテーブル用。

※Laravelの規約に従うため、モデル名は必ず単数形にします。

php artisan make:model Check -m

次に、右側のchecksテーブル用を作成します。

モデル

app/Inquiry.phpapp/Check.phpが作成されるので、中身を書いていきましょう。

app/Inquiry.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Inquirer extends Model
{
    protected $fillable = [
        'name',
    ];
}

app/Check.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Check extends Model
{
    protected $fillable = [
        'inquirers_id',
        'check_item',
    ];
}

マイグレーション

コマンドで-mを付けると、database/migrationsにマイグレーションファイルも一緒に作られるので、中身を書いていきます。

create_inquirers_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateInquirersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('inquirers', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('inquirers');
    }
}

create_checks_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateChecksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('checks', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('inquirers_id');
            $table->string('check_item');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('checks');
    }
}

ここまで完了したら、以下のコマンドでDBにテーブルが作成されます。

php artisan migrate

バリデーションの設定

app/Http/Controllers/InquiriesController.phpprocessの中に書いていきます。

public function process(Request $request)
    {
        // バリデーション
        $request->validate([
            'name'       => 'required',
            'check_item' => 'required',
        ]);
    }

index.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="container mt-5">
        <h1 class="text-center mb-5">お問い合わせ</h1>
        <div class="container">
            {!! Form::open(['route' => 'process', 'method' => 'POST']) !!}
                {{ csrf_field() }}
                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">お名前</p>
                    <div class="col-sm-8">
                        {{ Form::text('name', null, ['class' => 'form-control']) }}
                    </div>
                </div>
                {{-- エラー表示 --}}
                @if ($errors->has('name'))
                    <p class="alert alert-danger">{{ $errors->first('name') }}</p>
                @endif

                <div class="form-group row">
                    <p class="col-sm-4 col-form-label">選択(複数選択可)</p>
                    <div class="col-sm-8">
                        @foreach ($check_items as $key => $check_item)
                            <label>{{ Form::checkbox('check_item[]', $key) }}{{ $check_item }}</label>
                        @endforeach
                    </div>
                </div>
                {{-- エラー表示 --}}
                @if ($errors->has('check_item'))
                    <p class="alert alert-danger">{{ $errors->first('check_item') }}</p>
                @endif
                
                <div class="text-center">
                    {{ Form::submit('送信', ['name' => 'submit', 'class' => 'btn btn-primary']) }}
                </div>
            {!! Form::close() !!}
        </div>
    </div>
</body>
</html>

このままだと英語で表示されるので、日本語化していきます。

resources/langのenフォルダと同階層にjaフォルダを作成し、validation.phpをコピペ。

修正した箇所は以下の通りです。

'required' => ':attributeは必須項目です。',

'attributes' => [
        'name'       => 'お名前',
        'check_item' => '選択',
    ],

2つのテーブルにデータを保存する

app/Http/Controllers/InquiriesController.phpprocessにDBの2つのテーブルにデータを保存する動作を記述します。

use宣言も忘れずに書きましょう。

コントローラーの全体は、このようになりました。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Inquirer;
use App\Check;
use DB;

class InquiriesController extends Controller
{
    public function index()
    {
        $check_items = config('const.check_item');

        return view('inquiries.index', [
            'check_items' => $check_items,
        ]);
    }

    public function process(Request $request)
    {
        $request->validate([
            'name'       => 'required',
            'check_item' => 'required',
        ]);

        $input = $request->except('submit');

        try {
            DB::beginTransaction();

            // inquirersテーブルにデータを格納
            $inquiry = new Inquirer();
            $inquiry->fill($input);
            $inquiry->save();

            // inquirersのidを $inquiryId とする
            $inquiryId = $inquiry->id;

            // 登録されたチェックボックスの内容を配列で所持
            $inquiryData = $request->get('check_item');

            // checksテーブルにデータを格納
            foreach ($inquiryData as $inquiry) {
                $entryCheck = New Check();
                $entryCheck->inquirers_id = $inquiryId;
                $entryCheck->check_item   = $inquiry;
                $entryCheck->save();
            }

            DB::commit();

            return redirect()->route('complete');

        } catch (Exception $e) {
            DB::rollBack();
            return redirect()->route('inquiry')->withInput($input)->with('flash_message', 'エラーが発生しました。');
        }
    }

    public function complete()
    {
        return view('inquiries.complete');
    }
}

お問い合わせ一覧を表示する

お問い合わせフォームが完成したので、次にお問い合わせ一覧を表示しましょう。

コントローラーの作成

php artisan make:controller DashboardsController

app/Http/Controllers/DashboardsController.phpが作成されました。

ここでは、お問い合わせの一覧表示だけなので、indexのみfunctionを作ります。

そして、保存した2つのテーブルの中身を取得してビューに渡します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Inquirer;
use App\Check;

class DashboardsController extends Controller
{
    public function index()
    {
        // チェックボックスの内容の定数を取得
        $check_items = config('const.check_item');

        // 2つのテーブルの内容をすべて取得
        $inquiries = Inquirer::get();
        $checks    = Check::get();

        return view('dashboard', [
            'check_items' => $check_items,
            'inquiries'   => $inquiries,
            'checks'      => $checks,
        ]);
    }
}

ルーティングの設定

routes/web.phpに追記します。

// お問い合わせ一覧ページ
Route::get('/dashboard', 'DashboardsController@index')->name('dashboard');

ビューの作成

resources/viewsdashboard.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="container mt-5">
      <h1 class="text-center mb-5">お問い合わせ一覧</h1>
      <table class="table table-striped">
        <thead>
          <tr>
            <th scope="col">名前</th>
            <th scope="col">選択</th>
            <th scope="col">お問い合わせ日時</th>
          </tr>
        </thead>
        <tbody>
          @foreach ($inquiries as $inquiry)
            <tr>
              <td>{{ $inquiry->name }}</td>
              <td>
                @foreach ($checks as $check)
                {{-- inquiriesテーブルのidとchecksテーブルのinquirers_idが同じもののみ表示 --}}
                  @if ($inquiry->id == $check->inquirers_id)
                    {{ $check_items[$check->check_item] }}
                  @endif
                @endforeach
              </td>
              <td>{{ $inquiry->created_at }}</td>
            </tr>
          @endforeach
        </tbody>
      </table>
  </div>
</body>
</html>

以上で、同時に2つのテーブルにデータを保存&表示ができました。

お疲れさまでした\(^o^)/

【まとめ】同時に2つのテーブルにデータを保存して、表示する方法

2つのテーブルにデータをわけて保存する、というのを実務で扱うことがありました。

簡単なようで意外に苦戦したので、同じように悩んでいる方の参考になれば嬉しいです。

コードをすべて見たい!という方は、こちら(GitHub)からどうぞ。

以上です。
最後まで見てくださり、ありがとうございました。

Laravelでいろいろ作っていこうと思っているので、またアウトプットします。

» Laravelで作成したアプリやアウトプットの一覧はこちら

最後に宣伝

ココナラで、コーディングの相談を受け付けています。

  • CSSが上手く作れない
  • JavaScriptが思ったように動かない
  • ブログのデザインを修正したい
  • 勉強中でわからないところがあるから教えてほしい

このようなお悩みを解決していますので、「こんなの解決できる?」ということがあったら、ぜひ質問だけでも以下のリンクよりどうぞ。

HTML / CSS / JSのお悩みを解決します コーディングでお困りの方はお気軽にお問い合わせください! | Webサイト修正・カスタム・コンサル | ココナラ
HTML / CSS / JavaScript(jQuery)のお悩み相談、ご質問を受け付けます。「CSSが上手く作れない」「JavaScriptが思ったように...

コメント

タイトルとURLをコピーしました