【laravel】リレーションbelongsToManyの使い方

こんにちは、らいか(@hideiwa1)です。

今回は、laravelでテーブル同士を関連付ける【belongsToMany】の使い方を解説します。
これにより、多対多のテーブルの扱いが容易になります。

リレーションの基本

まずは、laravelにおけるリレーションの確認です。

リレーションとは、データベースのテーブル同士を関連づける機能です。
例えば、「users」テーブルと「items」テーブルを例にしてみましょう。

1対1

ユーザーが1つの品物だけを持っていて、その品物がすべて1つずつの場合、「users」テーブルと「items」テーブルの関係は「1対1」の関係です。

usersテーブル
id name
1 山田
2 加藤
3 佐々木
itemsテーブル
id name users_id 
1 りんご 1
2 みかん 3
3 バナナ 2

1対多

ユーザーが複数の品物を持っていて、その品物がすべて1つずつの場合、「users」テーブルと「items」テーブルの関係は「1対多」の関係です。

usersテーブル
id name
1 山田
2 加藤
3 佐々木
itemsテーブル
id name users_id 
1 りんご 1
2 みかん 3
3 バナナ 1

多対多

ユーザーが複数の品物を持っていて、その品物が複数存在する場合、「users」テーブルと「items」テーブルの関係は「多対多」の関係です。
今回使用するのは、こちらのケースになります。

usersテーブル
id name
1 山田
2 加藤
3 佐々木
item_userテーブル
user_id item_id
1 2
1 1
3 3
2 3
3 1
itemsテーブル
id name
1 りんご
2 みかん
3 バナナ
https://wp.festina-rente.com/relation-basics/

belongsToManyの使い方

テーブル設計

では、早速リレーションの設定をしていきましょう。

まずは、各テーブルを作成します。
「多対多」の場合、2つのテーブルを結びつける「中間テーブル」が必要となります。

中間テーブルを作成する際の注意点ですが、以下の通りです。

関連付けるテーブル名を「_(アンダースコア)」でつなげる

つなげるテーブル名は「単数形

つなげる順番は、「アルファベット順

各テーブルのIDをカラムに指定する
(今回の場合は「user_id」と「item_id」)

今回の場合、「item_user」テーブルとなります。

リレーションの設定

中間テーブルが作成出来たら、今度はモデルへのリレーションの設定です。

「多対多」の場合、「belongsToMany」メソッドを使って新しいメソッドを作成します。
userモデル、itemモデルのそれぞれに設定が必要となります。

belongsToMany(モデルクラス名、中間テーブル名、外部キー、関連づけるモデルの外部キー)
第2引数以降は省略可能です。


  app/Models/User.php

  public funcion items()
  {
      return $this->belongsToMany(Item::class);
  }

  app/Models/Item.php

  public funcion users()
  {
      return $this->belongsToMany(User::class);
  }

データを追加する

次に、中間テーブルへデータを追加していきましょう。
データを追加するには、「attach」メソッドを使用します。


  $user = User::find(1);

  $item_id = 2;

  $user->items()->attach($item_id);

こちらの例では、user_id:1、item_id:2 のデータが追加されます。

また、attach() の引数には配列を 指定することも可能です。

データを取得する

次に、リレーション先からデータを取り出してみましょう。

リレーション先のデータにアクセスする際には、プロパティ名を指定します。
中間テーブルのデータにアクセスする際には、「pivot」属性を使用します。

belongsToMany で関連付けられたモデルには、自動的に pivot 属性が割り当てられます。
この属性の中には、中間テーブルを示すモデルが含まれています。


  $user = User::find(1);

  foreach($user->items as $item)
  {
      echo $item->name;
      echo $item->pivot->item_id;
  }

データを更新する

中間テーブルのデータを更新する際には、「sync」メソッドを使用します。

syncメソッドを使用すると、引数に指定されたIDのみが中間テーブルに残るように更新されます。


  $user = User::find(1);

  $user->items()->sync([1,3]);
item_userテーブル
user_id item_id
1 1
1 2
3 2
2 3
3 1

item_userテーブル
user_id item_id
1 1
3 2
2 3
3 1
1 3

データを削除する

データを削除する際には、「detach」メソッドを使用します。

detachメソッドを使用すると、該当するデータを中間テーブルから削除します。
各モデルのデータは、そのままデータベースに残ります。


  $user = User::find(1);

  $user->items()->detach(1);
    //user_id:1、item_id:1のデータを削除する

  $user->items()->detach();
    //user_id:1のデータを全て削除する

使い方の応用例

中間テーブルに項目を追加する

初期状態では、中間テーブルには各モデルキーのみが存在します。
この中間テーブルに対して、追加の項目を設定したい場合は、リレーションの設定で項目の指定が必要となります。

項目を指定するには、「withPivot」メソッドを使用します。
また、created_at および updated_at のタイムスタンプを使用する場合は、「withTimestamps」メソッドを使用します。


  app/Models/User.php

  public funcion items()
  {
      return $this->belongsToMany(Item::class)
          ->withPivot('count', 'price')
          ->withTimestamps();
  }

保存する際は、attachの第2引数へ追加データの配列を指定します。
sync を使用する場合は、指定のIDに追加データを指定します。


  $user = User::find(1);

  $item_id = 2;

  $user->items()->attach($item_id, ['count' => 3]);

  $user->items()->sync([1 => ['count' => 3], 3]);

中間テーブルにメソッドを追加する

中間テーブルへメソッドを追加したい場合、中間テーブルのカスタムピボットモデルを定義する必要があります。

作成する際は、Model ではなく、Pivot を継承する点が注意です。


  app/Models/ItemUser.php

  namespace App\Models;

  use Illuminate\Database\Eloquent\Relations\Pivot;

  class ItemUser extends Pivot

  public funcion hello()
  {
     echo 'hello';
  }

次に、リレーションへ「using」メソッドを追加します。
これにより、中間テーブルのメソッドを使用することが出来るようになります。


  app/Models/User.php

  public funcion items()
  {
      return $this->belongsToMany(Item::class)
          ->using(ItemUser::class);
  }

メソッドを呼び出す際は、「pivot」属性を使用します。


  $user = User::find(1);

  foreach($user->items as $item)
  {
      echo $item->pivot->hello();
  }

まとめ

「多対多」のリレーションには「belongsToMany(Model::class)」

「table_table」の中間テーブルが必要

追加は「attach」、更新は「sync」、削除は「detach」

中間テーブルの要素を取得するには「pivot」

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です