この記事は、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.php
とapp/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.php
のprocess
の中に書いていきます。
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.php
のprocess
に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/views
にdashboard.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が思ったように動かない
- ブログのデザインを修正したい
- 勉強中でわからないところがあるから教えてほしい
このようなお悩みを解決していますので、「こんなの解決できる?」ということがあったら、ぜひ質問だけでも以下のリンクよりどうぞ。
コメント