この記事について
Laravel Advent Calendar 2018 - Qiita の14日目の記事です。
本記事ではコードは要点となる部分しか記述していないので、他の箇所も見たいという方はGitHubに公開したサンプル GitHub - kkznch/laravel_pivot_example を参照してください。
はじめに
概要
多対多リレーションを操作するための中間テーブルに三つ以上の関係を持たせた際に、pivot属性から関係先にどうやってアクセスするかについて書いていきます。
環境
- PHP 7.2.4
- Laravel 5.7.16
やりたいこと
テーブル構成が以下のようにあるとする。
上記のテーブル構成において、あるユーザが複数のグループに所属しているとき、それらのグループでそのユーザに割り振られているロールを以下のように取得したい。
$user->groups()->find(1)->pivot->role
実装手順
1. マイグレーションファイルを作成する
前述したテーブル構成通り id
, name
カラムを持つ users, groups, roles テーブルをそれぞれ定義する。
同様に user_id
, group_id
, role_id
を持つ中間テーブル user_group テーブルを定義する。これらのカラムは users, groups, roles テーブルを参照する外部キーとする。
2. Group, Role モデルクラスを作成する
php artisan make:model
コマンドで Group, Role モデルクラスをそれぞれ作成する。
なお、Group, Role モデルクラスの中身は空のままでよい。
3. UserRole Pivotクラスを作成する
以下のように UserRole Pivotクラスを作成する。
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserGroup extends Pivot
{
protected $table = 'user_group';
public function role()
{
return $this->belongsTo(Role::class);
}
}
継承しているのはModelクラスではなく、Pivotクラスなので注意すること。
4. User モデルクラスを作成する
ここが肝になる部分。
php artisan make:model
コマンドで User モデルクラスを作成し、以下のように編集する。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $fillable = ['name'];
public function groups()
{
return $this
->belongsToMany(Group::class, 'user_group')
->using(UserGroup::class)->withPivot(['role_id']);
}
}
using()
で先程指定した中間テーブルのカスタムモデルとなるPivotクラスを指定し、 withPivot()
でpivot属性を介して中間テーブルの指定した要素へのアクセスを可能にする。
使い方
php artisan tinker
を起動して以下を実行してみる。
>>> $user = User::find(1)
>>> $user->groups()->find(1)
=> App\Group {
id: 1,
name: "グループ1",
created_at: "2018-12-09 01:18:03",
updated_at: "2018-12-09 01:18:03",
pivot: App\UserGroup {#2894
user_id: 1,
group_id: 1,
role_id: 1,
},
}
>>> $user->groups()->find(1)->pivot->role
=> App\Role {
id: 1,
name: "ロール1",
created_at: "2018-12-09 01:18:03",
updated_at: "2018-12-09 01:18:03",
}
$user->groups()->find(1)
を実行した段階ではpivot属性には user_id
, group_id
, role_id
が含まれていることが分かる。
この状態で $user->groups()->find(1)->pivot->role
を実行すると、User モデルクラスで定義した groups()
から特定のグループに関する関係を取得し、更にpivot属性を介して UserRole Pivotクラスで定義した role()
からロールに関する関係を取得できる。
おわりに
以前からQiitaに登録はしていたものの投稿はしていなかったため、今回が初の投稿となりました。
いつもは人が書いた記事を遠目で見て参考にしているだけでしたが、書いてみるのも割といいものだなと思いました。
記事中の実装や考え方について、間違っている or 他に良い方法などがあれば教えていただければ幸いです。