Help us understand the problem. What is going on with this article?

【Laravel】多対多の中間テーブルのモデルはPivot化するとアクセスしやすくなりメソッドを使いやすい

More than 1 year has passed since last update.

はじめに

多対多の関係の中間テーブルのモデルはPivot化すると、リレーション経由でアクセスしやすくなります。

中間テーブルのモデルにメソッドを持たせていて、それを使いたい場合に便利です。

Pivot化していない場合

例として、Itemモデルに、多対多の関係にあるTagモデルが存在し、以下のような多対多のリレーションが定義されているとします。

App\Item.php
class Item extends Model
{
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany('App\Tag');
    }
}

このリレーションを使って、Tagモデルを取得してみます。

$ php artisan tinker
>>> App\Item::find(1)->tags
=> Illuminate\Database\Eloquent\Collection {#2901
     all: [
       App\Tag {#2894
         id: 1,
         name: "タグ1",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#2892
           item_id: 1,
           tag_id: 1,
         },
       },
     ],
   }

リレーションを使って取得したTagモデルには、

  • pivotプロパティ

があり、そこには

  • Illuminate\Database\Eloquent\Relations\Pivot

が格納されていることがわかります。

さて、中間テーブルのモデルItemTagに何かメソッドを持たせていたとして、これを使うにはどうすれば良いでしょうか。

App/ItemTag.php
class ItemTag extends Model
{
    public function hello(): string
    {
        return 'Hello, world.';
    }
}

以下のようなことをやっても上手くはいきません。

$ php artisan tinker
>>> App\Item::find(1)->tags->first()->pivot->hello()
BadMethodCallException with message 'Call to undefined method Illuminate/Database/Query/Builder::hello()'

Pivot化する

まず、先ほどの定義済みのリレーションにusingメソッドを使うようにします。

App\Item.php
class Item extends Model
{
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany('App\Tag')
            ->using('App\ItemTag'); // 追加
    }
}

その上で、中間テーブルのItemTagをモデルではなくPivotにします。

App/ItemTag.php
class ItemTag extends Pivot // ModelからPivotに変更
{
    public function hello(): string
    {
        return 'Hello, world.';
    }
}

こうすることで、リレーションを使って取得したTagモデルのpivotプロパティには

  • Illuminate\Database\Eloquent\Relations\Pivot

ではなく、

  • ItemTag

が格納されるようになります。

$ php artisan tinker
>>> App\Item::find(1)->tags->first()
=> App\Tag {#2868
     id: 1,
     name: "タグ1",
     pivot: App\ItemTag {#2862 // ここに注目!
       item_id: 1,
       tag_id: 1,
     },
   }
>>> App\Item::find(1)->tags->first()->pivot
=> App\ItemTag {#2865 // ここに注目!
     item_id: 1,
     tag_id: 1,
   }

これで、ItemTagにリレーション経由でアクセスし、そのメソッドを使えるようになりました。

>>> App\Item::find(1)->tags->first()->pivot->hello()
=> "Hello, world."

補足

  • もし、Pivot化はせず、リレーションにusingメソッドを使っただけの状態だと、以下のエラーになります。
>>> App\Item::find(1)->tags->first()->pivot
BadMethodCallException with message 'Call to undefined method Illuminate/Database/Query/Builder::fromRawAttributes()'
  • Pivotは、Modelを継承しています。
/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Pivot.php
class Pivot extends Model
{
// 略

参考

sencorp
幼稚園・保育園向けインターネット写真サービス「はいチーズ!」を提供しています。
https://sencorp.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away