はじめに
多対多の関係の中間テーブルのモデルはPivot化すると、リレーション経由でアクセスしやすくなります。
中間テーブルのモデルにメソッドを持たせていて、それを使いたい場合に便利です。
目次
Pivot化していない場合
例として、Item
モデルに、多対多の関係にあるTag
モデルが存在し、以下のような多対多のリレーションが定義されているとします。
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
に何かメソッドを持たせていたとして、これを使うにはどうすれば良いでしょうか。
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
メソッドを使うようにします。
class Item extends Model { public function tags(): BelongsToMany { return $this->belongsToMany('App\Tag') ->using('App\ItemTag'); // 追加 } }
その上で、中間テーブルのItemTag
をモデルではなくPivotにします。
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を継承しています。
class Pivot extends Model { // 略