とある不動産検索システムの案件で、
物件ごとに、
設備の情報(例えばシステムキッチンとか角部屋とか)を持たせておいて、
一覧画面では設備の情報を3つ〜5つ見せつつ、
詳細の画面では設備の情報を全部表示させたい。
という要望がありまして。
ふと疑問に思ったのでメンターに相談してみました。
いくつかのやりとり後、メンターから返信あり、
ということで、中間テーブルを2つ以上つくる方法をまとめてみます。
Laravel 5.5 で実施しています。
Laravel 多対多テーブルを2つつくる方法 まずはモデル
今回の構成です。
Propertiesテーブルが物件ごとの情報で、
Optionsテーブルに、設備の情報をずらっと並べています。
中間テーブルとして、
- option_property テーブル と
- optiondisplay_property テーブル
をそれぞれつくります。
まずはモデルから。
// Property.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Property extends Model { public function options() { return $this->belongsToMany('App\Models\Option','option_property','property_id','option_id'); } public function option_display() { return $this->belongsToMany('App\Models\Option', 'optiondisplay_property', 'property_id', 'option_id'); } }
多対多なのでbelongsToManyなのですが、
- 第一引数に相手側のモデル名
- 第二引数に中間テーブル名
- 第三引数に接続元モデルID (中間テーブルで使われる名称)
- 第四引数に接続先モデルID (中間テーブルで使われる名称)
という順番で明記します。
// Option.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Option extends Model { // public function properties() { return $this->belongsToMany('App\Models\Property'); } public function propertylists() { return $this->belongsToMany('App\Models\Property'); } }
こっちはあっさり。
Laravel 多対多テーブルを2つつくる方法 マイグレーション
続いてマイグレーション(データベースのテーブル情報)。
// create_properties_table.php <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePropertiesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('properties', function (Blueprint $table) { $table->increments('id'); //抜粋 }); } }
多対多の場合、モデル同士は特に追記することはないです。
// create_options_table.php <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateOptionsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('options', function (Blueprint $table) { $table->increments('id'); }); } }
// create_option_property_table.php <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateOptionPropertyTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('option_property', function (Blueprint $table) { $table->unsignedInteger('option_id'); $table->unsignedInteger('property_id'); $table->primary(['option_id', 'property_id']); //FK rule $table->foreign('option_id') ->references('id') ->on('options') ->onDelete('cascade'); $table->foreign('property_id') ->references('id') ->on('properties') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('options'); } }
リレーションでつなぐためにはデータの形を合わせる必要があり、
integerだとマイナスも含んでしまうため、
マイナスを含まないデータ型の unsignedInteger で指定する必要があります。
また、接続元のデータが削除された時に、合わせて中間テーブルの情報も削除したいので、
onDelete(‘cascade’) とつけることで、削除したときに同期して削除するようにしています。
もう一方の optiondisplay_property_table も同様に。
// create_optiondisplay_property_table.php <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateOptiondisplayPropertyTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('optiondisplay_property', function (Blueprint $table) { $table->unsignedInteger('option_id'); $table->unsignedInteger('property_id'); $table->primary(['option_id', 'property_id']); //FK rule $table->foreign('option_id') ->references('id') ->on('options') ->onDelete('cascade'); $table->foreign('property_id') ->references('id') ->on('properties') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('options'); } }
これで php artisan migrate とやるともりっとテーブルが作成されます。
Laravel 多対多テーブルを2つつくる方法 ビュー側
今回はチェックボックス想定で、
@foreach で回しつつ、nameの箇所は配列にする必要があります。
//抜粋しとります @foreach($options as $option) <input type="checkbox" name="options_display[]" value="{{$option->id}}">{{$option->name}} @endforeach @foreach($options as $option) <input type="checkbox" name="options[]" value="{{$option->id}}">{{$option->name}} @endforeach
Laravel 多対多テーブルを2つつくる方法 コントローラー側
コントローラーがちょっと悩ましく、
果たしてこれがベストなのかというと怪しいなぁと思いつつ、
動くことは動いたのであえて公開します。
public function store(Request $request) { $property = new Property(); // 一旦セーブ $property->save(); //保存したidを取得 $property_latest_id = DB::table('properties') ->latest() ->select('id') ->first(); $property_id = $property_latest_id->id; // フォームから要素取得 $options_display = $request->options_display; $options = $request->options; //保存したidのPropertyインスタンスを生成 $property_latest = Property::findOrFail($property_id); // sync で中間テーブルに同期保存 $property_latest->option_display()->sync($options_display); $property_latest->options()->sync($options); return redirect()->to('home');
新規登録のタイミングだと、登録したタイミングでないとProperty側のid番号がわからないので、
- 一旦Propertyモデルをセーブ
- 保存したidを取得
- リクエスト情報を取得
- 保存したidのPropertyインスタンスを生成
- syncで中間テーブルに同期保存
としています。
Laravel 多対多テーブルを2つつくる方法を実際に試してみて
保存のメソッドがこれでいいのか、という疑問はのこりつつも、
多対多テーブルを2つ以上自由につくれるようになったというのはとても大きくて、
いろんなバリエーションのデータベース構成がつくれるようになるなと実感しました。
『Laravel』ではこんな記事も読まれています。
1. 【Laravel】マルチログイン対応ECサイトの講座をリリースしました【Udemy】2. 【PHP/Laravel】初心者向けの動画をリリースしました【Udemy】
3. 【Laravel(PHP)】初心者向け アプリのつくり方 をリリースしました【techpit】
4. 『Carbon』でよく使うパターンをまとめてみた【Laravel向け】
5. 【Laravel(PHP)】でできる事をわかりやすく(ざっくりと)まとめてみた【用語集も兼ねて】【初心者向け】
6. 【Laravel】フロントエンドをわかりやすくまとめてみた【初心者向け】
7. 【PHP】【Laravel】CSVエクスポートの方法〜5つのポイント〜
8. 【PHP】CSVインポートの方法〜大量データもバルクインサートでバッチリ!〜【laravel】
9. 【Laravel】ダミー(テスト)データを作る方法 シーダー(seeder)とfactoryとfaker【初心者向け】
10. 【Laravel】Webアプリ環境構築の仕方【Vue.js】【初心者向け】
11. 【Laravel】と【Vue.js】のサンプル動画を見ながらさらりと解説してみる
12. 【Laravel】マルチログイン(ユーザーと管理者など)機能を設定してみた【体験談】
13. 【Laravel】フォトギャラリーを作るための画像アップロード方法【php】
14. 【Laravel】多対多テーブルを複数つくる方法【ちょっとコツがいります】
15. 【Laravel】【Slack】に通知する方法をまとめてみた【自作ファサード】
アオキのツイッターアカウント。
この記事へのコメントはありません。