こんにちは、らいか(@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 | バナナ |
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」
コメントを残す