バックエンド

【Laravel】多対多テーブルを複数つくる方法【ちょっとコツがいります】

とある不動産検索システムの案件で、

物件ごとに、

設備の情報(例えばシステムキッチンとか角部屋とか)を持たせておいて、

一覧画面では設備の情報を3つ〜5つ見せつつ、

詳細の画面では設備の情報を全部表示させたい。

という要望がありまして。

アオキ
多対多テーブルを使えばいいと思うんだけど、多対多テーブルを一つ作るだけでいいんかな・・

ふと疑問に思ったのでメンターに相談してみました。

いくつかのやりとり後、メンターから返信あり、

メンター
不動産に紐付けるタグとは別に、一覧で表示用のタグを紐付けるのはOKです?
アオキ
tagテーブルとは別で、まったく同じ内容の 表示用のテーブルを作る、ということでしょうか、
メンター
tagテーブルはマスターデータなので、中間テーブルを紐付け用と表示用で作ってあげるイメージです。
アオキ
中間テーブルを別でつくるんですね! なるほど・・

ということで、中間テーブルを2つ以上つくる方法をまとめてみます。

Laravel 5.5 で実施しています。

Sponsored link

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当初は何が何だかでした、成長したよなぁ。
Sponsored link

Laravel 多対多テーブルを2つつくる方法 ビュー側

今回はチェックボックス想定で、
@foreach で回しつつ、nameの箇所は配列にする必要があります。

//抜粋しとります

        @foreach($options as $option)
            <input type="checkbox" name="options_display[]" value="{{$option->id}}">{{$option->name}}&nbsp;&nbsp;&nbsp;
        @endforeach


        @foreach($options as $option)
            <input type="checkbox" name="options[]" value="{{$option->id}}">{{$option->name}}&nbsp;&nbsp;&nbsp;
        @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で中間テーブルに同期保存

としています。

アオキ
本当は同時に保存できる方法がありそうなんですが、なかなかしっくりくる方法が見つからず・・
Sponsored link

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】に通知する方法をまとめてみた【自作ファサード】

アオキ
ツイッターでも記事ネタ含めちょろちょろ書いていくので、よろしければぜひフォローお願いしますm(_ _ )m

アオキのツイッターアカウント


関連記事一覧 (一部広告あり)

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

CAPTCHA


最近の記事

アーカイブ

  1. オンライン教材

    ChatGPTをビジネス活用する講座をリリースしました【Udemy】
  2. 生成AI

    Dify講座をリリースしました【非エンジニア向け】
  3. バックエンド

    【Laravel第4弾】Vue.js3(CompositionAPI+Scrip…
  4. データベース

    MySQLの講座をリリースしました
  5. オンライン教材

    【ChatGPT】エンジニア編をリリースしました
PAGE TOP
Ads Blocker Image Powered by Code Help Pro

広告ブロックを摘出しました!!

ブラウザ拡張を使用して広告をブロックしていることが摘出されました。

ブラウザの広告ブロッカーの機能を無効にするか、
当サイトのドメインをホワイトリストに追加し、「更新」をクリックして下さい。

あなたが広告をブロックする権利があるように、
当方も広告をブロックしている人にコンテンツを提供しない権利と自由があります。

Powered By
Best Wordpress Adblock Detecting Plugin | CHP Adblock