#事の顛末
LaravelのクエリビルダでJOINしてテーブルの値を持ってきたときに、JOINで結びついた2つのテーブルが同じ名前のカラムを持っていた場合、従テーブルの値で上書きされて死んだ。
みなさんもお気をつけてください。
#もっと詳しく説明
例えば、多対多の関係でUserテーブルとGroupテーブル、その関係を表す中間テーブル(User_Groupテーブル)があるとしましょう。
Userは色んなグループに所属でき、グループには複数のUserがいるって感じですね。
テーブルはこんな感じ。
■Userテーブル
id | name | is_deleted |
---|---|---|
1 | 太郎くん | 1 |
2 | 次郎くん | 0 |
■Groupテーブル
id | name | is_deleted |
---|---|---|
1 | 図書委員 | 0 |
2 | 保険委員 | 0 |
3 | 体育委員 | 0 |
■User_Groupテーブル(中間テーブル)
id | user_id | group_id |
---|---|---|
1 | 1 | 1 |
2 | 2 | 2 |
※id_deltedは論理削除用の適当なフラグだとでも考えてください。
こんなときに、図書委員という名前のグループに所属していて、かつis_deletedが1のUserを全て持ってきたいと思ったときにみなさんどうします?
やり方はパッと2通りあるかと思います!
その1、 whereHas関数を使う
Userモデルクラスを作成して、その中で下記のようにwhereHasを使って子テーブルのカラムを指定してあげればOKです。1対多でも多対多でも大丈夫みたいですね。
$userModel
->where('is_deleted',1)
->whereHas('group', function ($query) use () {
$query->where('name', 'like', '%図書委員%');
})->get();
これが一番わかりやすくて良いと思うのですが、どうやらwhereHasは処理が遅いらしく、whereHasの使用を禁じてJOINを使って解決しましょうというルールを今回敷かれました。(Laravel公式でこれを使えと言ってるのですがそれは)
そこで次です。
その2、JOINで頑張る。
return DB::table('User')
->join('User_Group', 'User.id', '=', 'User_Group.user_id')
->join('Group', 'User_Group.group_id', '=', 'Group.id')
->where('User.is_deleted', '=', 1)
->where('Group.name', 'like', '%図書委員%')
->get();
Userテーブルに対して中間テーブルをJOINし、続けてGroupテーブルもJOINします。
その上でwhere条件で絞ってあげれば大丈夫でしょう。
・・・と思って返却されたコレクションの値を見たら
■返却されたコレクション
#items: Collection {#2384 ▼
#items: array:1 [▼
0 => {#2380 ▼
+"id": 1
+"is_deleted": 0
+"created_at": "2018-04-17 19:22:00"
+"updated_at": "2018-04-17 19:22:00"
+"group_id": 1
}
]
}
あれ!?where条件でis_deletedに1を指定して取ってきたのに、返却されてきたコレクションにはis_deletedが0で入ってる!!
どうやらGroupテーブルにある同一カラム(is_deleted)の値で上書きされてしまっているようです。こんなん引っ掛かるよ...
これの詳しい仕組み知っている人いたら教えてください〜〜〜!!
#解決策
selectする値を明示的に指定する
return DB::table('User')
->join('User_Group', 'User.id', '=', 'User_Group.user_id')
->join('Group', 'User_Group.group_id', '=', 'Group.id')
->where('User.is_deleted', '=', 1)
->where('Group.name', 'like', '%図書委員%')
->select('User.is_deleted') //これを追加
->get();
のように、selectを追加してどのテーブルのカラムかを明示的に指定してあげればOKでした。
whereHasがスロークエリだということですがどれくらい遅いんでしょうかねえ。。。
まあJOINを使わなきゃいけない状況に陥ったときに参考にでもどうぞ