PHP
Laravel

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


はじめに

多対多の関係の中間テーブルのモデルは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

{
// 略


参考