🦀

laravel7.x¦リレーション先の条件も加味した複雑なwhere条件のデータを取得する

2021/03/16に公開

やりたいこと

リレーションを使って複雑なwhereをいい感じ(ざっくり)に取得していく方法のメモですφ('ᴗ'」)

当然ながらjoinなどを使ったり、他の方法でも同じようなデータは取得できると思います。
今回はその中でもリレーションを頑張って使っていく方法です。メリットとしてはリレーション先のデータへのアクセスがスマートにできる、コード数が少なくなる(ような気が)します。

今回やっていくことはこちら💡
・ A or B の時 Aはリレーション先のテーブルの条件の場合
・(A and B and C and D) or (C and D and E)の時 AとBはリレーション先のテーブルの条件の場合

環境

・laravel 7.1
・php 7.3

前提

塾とか何か習いごとのクラスをしているかのようなテーブルを想像していただければと思います💡
グループクラス(group_class_infosにデータがあるもの)は複数の先生に見てもらえるリッチな塾という感じで...

・companies - 先生の所属する塾
・teachers - 先生のテーブル
・students - 生徒のテーブル
・classes - 先生と生徒1on1のクラス
・group_class_infos
  - 複数の先生と生徒1人のクラス
  - 先生が対象のグループクラスに参加/不参加/回答待ちのステータスも保存

想像しやすいように図を作ってみたのですが、鳥の足記法がもし間違っていたらごめんなさい😹
ER図

グループクラス、メインの先生、サブの先生とは? テーブルの仕様について...

💡グループクラスになっている場合
 group_class_infosのclass_idカラムにclassesのidのデータがある
 
💡メインの先生の見つけ方
 group_class_infosのclass_idカラムにclassesのidのデータがある場合、classesのteacher_idがメインの先生
 
💡サブの先生の見つけ方
 group_class_infosのclass_idカラムにclassesのidのデータがある場合、group_class_infosのteacher_idのみがサブの先生
※メインの先生のデータもgroup_class_infosに入ってきます

Class.phpでstudentsとgroup_class_infosのリレーションを定義しておいてください💡
今回はstudentsはstudentで group_class_infosはgroupInfosとしています( '֊' )

A or B の時 Aはリレーション先のテーブルの条件の場合

一人の先生のクラスの予定で、個人クラスもグループクラスもとにかく全部ほしいとき

$classes = Class::with('student')
    ->with('groupInfos')
    ->whereHas('groupInfos', function ($q) use ($teacherId) {
        $q->where('group_class_infos.teacher_id', $teacherId);
    })
    ->orWhere('classes.teacher_id', $teacherId)
    ->get();

with('student')としているので生徒の名前も取り出すことができます💫

@foreach ($classes as $class)
    {{ $class->student->isNotEmpty() ? $class->student->name : '未予約' }}
@endfoeach

リレーションを定義しているとwithで指定するだけでいい感じにデータがとれるので便利ですね!

collectionクラスで後続処理を行う場合にグループクラスの他の先生の情報は不要な場合

with('groupInfos')としているところの条件を絞ると他の先生のデータが入ってこないのでスッキリすることがあります💡

$classes = Class::with('student')
    ->with(['groupInfos' => function ($q) use ($teacherId) {
        $q->where('group_class_infos.teacher_id', $teacherId);
    }])
    ->whereHas('groupInfos', function ($q) use ($teacherId) {
        $q->where('group_class_infos.teacher_id', $teacherId);
    })
    ->orWhere('classes.teacher_id', $teacherId)
    ->get();

🏳 🏳 🏳

(A and B and C and D) or (C and D and E)の時 AとBはリレーション先のテーブルの条件の場合

先生のクラスの予定で、生徒から予約が入っている個人クラスとグループクラス両方がほしいとき

つまり以下のようなデータを取得したい時です💡
・自分の個人クラスで、生徒から予約が入っているクラス
・生徒から予約が入っているグループクラスで、自分がメインの先生のクラス
・生徒から予約が入っているグループクラスで、サブの先生で加えられており参加予定のクラス

$classes = Class::with('student')
    ->whereHas('groupInfos', function ($q) use ($teacherId, $status) {
        $q->where('group_class_infos.teacher_id', $teacherId)
            ->where('group_class_infos.status', $status);
    })
    ->whereNotNull('student_id')
    ->where('date', '>=', $date)
    ->orWhere(function ($q) use ($teacherId, $date) {
        $q->whereNotNull('student_id')
            ->where('date', '>=', $date)
	    ->where('teacher_id', $teacherId);
    })
    ->get();

🎩 🎩 🎩

おわりに...

実際に実装している時はリレーションもふんわりしかわかっていなかったこともあり
条件が複雑で迷走しまくり実装に時間がかかりましたが、こうしてまとめてみると簡単なような...あの時間は一体...

なんとか意図した動作になったので良かったです( '֊' )

Discussion