はじめに
多対多の関係の中間テーブルのモデルは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
{
// 略