びくんびくんしながらコードを書く。

いしきひくい系エンジニアのらくがき帳

laravel 5.5 多対多のリレーションで中間テーブルのデータを取得する

今回もLaravelのお話です。

調べてもあまり出てこない部分だったので得た知見をoutputしようと思います。

発端

大きく詰まったわけではないですが、多対多のつなぎ方はよく出てくるのに中間テーブルのデータを取得したいみたいな話はあんまり出てきてないので記事にまとめておこうと思いました。

どんな状況?

よくあるのがユーザが所属するグループがあって、グループ単位に管理者をユーザに付与する、管理者は複数存在して良いとかそんな感じだと思います。

通常の多対多

ER図としてはこんな感じだと思います。 f:id:bikun_bikun:20180913132140p:plain

laravelで表現すると・・・

modelでは中間テーブルを用意せず、userモデルとgroupモデルを作ってやります。

userモデル側での紐づけ方。
ドキュメントにも書いてあるとおりそんなに難しくありません。

    public function group(){
        return $this
            ->belongsToMany('App\Group');
    }

groupモデル側での紐づけ方。
こちらもuserモデルと同じように記載すればよいだけです。

    public function user(){
        return $this
            ->belongsToMany('App\User');
    }

上記のようにモデルに実装したらあとはそこまで難しくありません。 コントローラ側で呼び出すときは下記の様な実装を行います。

自分のidを1と仮定して読んでください。

//自分の所属するグループを取得
$myGroups = User::find(1)->group;

foreach($myGroups as $group){
    Log::info($group->toArray());
}

中間テーブル名とか入れてないけどどうなってるの?という部分について・・・
laravelではアルファベット順でモデルをつなげることで中間テーブルのデフォルト名にする。
また、各つながり部分はモデル_idで表現することをデフォルトとするという挙動を取るようです。

もし中間テーブル名が逆順でuser_groupにしちゃった!とか紐づけキーの名前を別にしてしまった!なんてことがあっても慌てなくて大丈夫です。

belongsToManyの第二引数以降でそのへんを設定することができます。 第二引数以降は下記です。

  • 第二引数:中間テーブル名
  • 第三引数:自モデルと中間テーブルを紐付ける中間テーブルのカラム名
  • 第四引数:相手モデルと中間テーブルを紐付ける中間テーブルのカラム名

中間テーブルに紐づけ以外の情報が入った場合の多対多

ER図は下記としましょう。 f:id:bikun_bikun:20180913134047p:plain

中間テーブルにis_administratorというカラムを追加しました。 中間テーブルのデータで紐付いているユーザがadministratorかどうかを判断したい場合は下記のような実装になります。

userモデルの修正。

    public function group(){
        return $this
            ->belongsToMany('App\Group')
            ->withPivot('is_administrator');
    }

groupモデルの修正

    public function user(){
        return $this
            ->belongsToMany('App\User');
            ->withPivot('is_administrator');
    }

コントローラで利用するときは下記のような実装にします。

//自分の所属するグループを取得
$myGroups = User::find(1)->group;

foreach($myGroups as $group){
    if($group->pivot->is_administrator){
        echo "あなたはグループ:" . $group->name . "の管理者です。";
    }
}

意外と簡単でした。

中間テーブルのデータ更新は?

現在その部分を絶賛調査中なので分かり次第また記事にしたいと思います。

以上