人気のPHP WEBアプリケーションフレームワークLaravelのTipsを記録していきます

● Laravel で 多対多 のリレーションを扱う

● Laravel全リレーション

1対1
1対多
多対多
Has Many Through
1対1(ポリモーフィック)
1対多(ポリモーフィック)
多対多(ポリモーフィック)

これらのうち 多対多 リレーションを操作してみます。

● Laravel の 多対多リレーションのDB構造

とても簡単です。2つのテーブルをつなぐピボットテーブルを作成すればOKです。

・users
・practitioners

というテーブルを多対多でつなぐ時は
テーブル ↓

・user_practitioner

を作成します。( practitioner_user でも良い。 アルファベット順に並べると規則がはっきりして良いですね。単数形 をアンダースコアでつなげます。)
中身は

テーブル「practitioner_user」の構造

id                  (primary key)
user_id             (integer,unsigned)
practitioners_id    (integer,unsigned)

とします。

マイグレーションファイルの作成

php artisan make:migration create_practitioner_user_table

マイグレーションファイルの up() メソッドは次のようになります。

    public function up()
    {
        Schema::create('practitioner_user', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('practitioner_id');
            $table->unsignedInteger('user_id');
            $table->timestamps();
        });
    }

● Laravel の 多対多リレーション(belongsToMany)をモデルに設定する

多対多リレーションある意味シンプルです。
これを両方のテーブルに(メソッド名は適宜変更して)定義します。
例)Userモデルに設定

    /**
     * ● 多対多 リレーション : ->practitioners でXXXXXを取得する
     * ピボットテーブル「practitioner_user」
     *
     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
     */
    public function practitioners()
    {
        return $this->belongsToMany('App\Practitioner','practitioner_user');
    }

メンバ名を ->practitioners から ->mypractitioners に にしたい場合は、そのまま

    public function my_practitioners()

とします

● 「検索条件」」「ソート順」を設定して Laravel の 多対多リレーション(belongsToMany)をモデルに設定する

    /**
     * ● 多対多 リレーション
     * ピボットテーブル : practitioner_user
     * ソート順        : id DESC
     *
     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
     */
    public function practitioners()
    {
        return $this->belongsToMany('App\Practitioner','practitioner_user')->where('is_active', '=', 1)->orderBy('id','DESC');
    }

●多対多リレーションのIDリストを取得する

$model = User::findOrFail($id);
dump( $model->practitioners()->allRelatedIds() );   // (注意)Laravel 5.4以前は getRelatedIds() という名前でした

●多対多リレーションの項目名( カラム名 = practitioner_name )をカンマ区切りで取得する

ユーザ1 , ユーザ2 , ユーザ3 の様な文字列を取得します

$model = User::findOrFail($id);
dump( $model->practitioners()->implode('practitioner_name' , ' , ' ) );

bladeテンプレートに記述するときは以下のようにします。

{!! $consentform->patient->managers->pluck('name')->implode('<br>') !!}

このようにも記述できます。( ↑ と同じです。)

{!! $consentform->patient->managers->implode('name','<br>') !!}

●多対多リレーションの項目名へのリンクを作成する

@php
	$consentform->patient->managers->each(function ($item) {
		$route_manager = route('admin.managers.show', $item->id);
		print <<< DOC_END
			<a href="{$route_manager}" target="_blank">{$item->name}</a><br>
		DOC_END;
	});
@endphp

●多対多リレーションに新たに紐づけする(attach)

$model = User::findOrFail($id);
$model->practitioners()->attach([5,6,7]);    // ピボットテーブルに(すでにあるデータは消さずに)データを追加

●多対多リレーション紐づけを解除する(detach)

$model->practitioners()->detach(1) // id=1の紐付けを解除する

●多対多リレーションをまとめての紐づけと紐づけの解除を行う(sync)

$model = User::findOrFail($id);
$model->practitioners()->sync([11,22,33]);    // ピボットテーブルを指定した配列のデータで更新(すでにあるデータを書き換え)

リレーションを一気に削除する場合は

->sync();

● データコンバートの時にリレーションを追加する(すでにある場合は何もしない)

// 既に持っているリレーションデータをすべて取得
$tmp_all_list = $c->practitioners()->pluck('practitioners.id')->toArray(); // [1,2,3,4,5,6]
if (! in_array($row[0],$tmp_all_list) ){
    array_push($tmp_all_list,$row[0]);
    $c->practitioners()->sync($tmp_all_list);    // ピボットテーブルを指定した配列のデータで更新(すでにあるデータを書き換え)
}

参考: pivot table に他のカラムデータを保存させる https://stackoverflow.com/questions/48323971/laravel-saving-to-pivot-table-in-many-to-many-relationship-with-extra-column

参考 : php - Laravel sync Relation with optional parameters - Stack Overflow

No.1452
05/07 13:57

edit