LoginSignup
11
8

More than 3 years have passed since last update.

LaravelでクエリビルダのJoinを使って検索機能を作ろうとしたらハマった

Posted at

当方、Laravel初心者です。

Eloquentモデルをクエリビルダのjoin()を使って関係するテーブルのカラムを使って絞り込んだら、
検索対象じゃないモデルのインスタンスが返ってきて小一時間悩みました。

まとめ

Eloquentモデルをクエリビルダのjoin()を使ってインスタンスを取得したら、
idというカラムが複数存在したため、
想定外のインスタンスが返ってきた。

同じカラム名はLaravel内の処理で上書きされる。
Select()で取得するカラムを絞ろう!

詳細

たとえばこんなコード。

$searched_posts = Post::join('comments', 'posts.id', '=', 'comments.posts_id')
  ->where('comments.text', 'like', '%あいうえお%')->get();

postsテーブルにもcommentsテーブルにも「id」というカラムがある前提です。

上記のコードをSQLに変換されると多分こんな感じになります。

select * from posts 
  inner join comments on posts.id = comments.post_id 
  where comments.text LIKE '%あいうえお%';

仮にSQLの実行結果が以下のような感じだったとします。

id post_content id text
1 あああ 2 あいうえお
3 ううう 3 あいうえお
5 おおお 4 あいうえお

これでPostモデルのインスタンスが3つ取れて、
post_contentはそれぞれ「あああ」「ううう」「おおお」となるんだろうな とおもったら
なぜか全然違うPostが返ってきました。

そうです。
idカラムが重複して存在するため、
posts.idcomments.idが上書きしてしまい、
id2,3,4であるPostがモデルインスタンスとして取得されてしまいました。

解決策

selectで取ってくるカラムを絞る。

$searched_posts = Post::select('posts.*')->join('comments', 'posts.id', '=', 'comments.posts_id')
  ->where('comments.text
', 'like', '%あいうえお%')->get();

select('posts.*')のように、取得するカラムを絞り込むことで、idというカラムが重複しないようにしましょう。

こういうハマりポイントは知っていればすぐ解決できるのに、知らないと結構はまってしまいますよね。
Laravelを覚えるにあたって先にハマりポイントを押さえておこうと思いました:frowning2:

そもそもクエリビルダのJoinを使って検索をするっていうのがイケてないっぽいので
よりよい方法があれば教えていただきたく思います。

whereHas()を使うといいのかな?

Post::whereHas('Comment', function($comment){
    $comment->where('text', 'like', '%あいうえお%');

以上、お役に立てれば幸いです

参考

laravelのクエリビルダでJOINしたら、同名カラムの値が上書きされるという事案発生
Laravel Eloquent でハマらないために知っておきたいこと

11
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
8