Laravel Whereの使い方をケース別にまとめ

LaravelのEloquentは、シンプルなデータ操作が可能なORMです。今回は、Eloquentのwhere系メソッドの使い方をケース別にまとめました。
題材として、よくあるブログのデータをサンプルに、where系メソッドの使い方を説明します。

ソースコードは、こちらにアップしています。Seedでテストデータも作成できますので、すぐに試すことができます。

※ 本記事のサンプルはLaravel5.6で動作確認済みです。

テストデータ概要

ER図

サンプルとして上記のデータベースを利用します。
記事のデータ(Posts)があり、一人の著者と複数のタグが紐づくデータを想定しています。
記事と著者は1対多、記事とタグは多対多の関係となります。

部分一致

記事のタイトルに対して、あるワードが含まれる記事を検索したいケースなどを想定しています。
Eloquentのwhereを使った実装は下記のようになります。
部分一致で検索したいため、whereの第二引数にLIKEを設定します。

$title = 'velit';
$posts = Post::where('title', 'LIKE', "%{$title}%")->get();

実際に実行されるSQLは下記のようになります。

select * from `posts` where `title` LIKE %velit%";

複数ワードの部分一致

検索ワードをスペースで区切り、複数のワードが含まれる記事を検索したいケースなどを想定しています。
Eloquentのwhereを使った実装は下記のようになり、where条件をandで繋げます。

$titles = ['velit', 'voluptatibus'];

$posts = Post::where(function ($query) use ($titles) {
    foreach ($titles as $title) {
        $query->where('title', 'LIKE', "%{$title}%");
    }
})
->get();

実際に実行されるSQLは下記のようになります。

select * from `posts` where (`title` LIKE "%velit%" and `title` LIKE "%voluptatibus%");

検索条件のネスト A and (B or C)

A and (B or C) のように検索条件をネストさせます。
例として、あるユーザーの記事 かつ タイトルまたは本文に任意のワードが含まれる記事を取得してみます。
このような場合は、Eloquentのwhereに関数を渡してあげると、where条件をグループ化して設定することができます。

$user = User::inRandomOrder()->first();

$posts = Post::where('author_user_id', $user->id)
    ->where(function ($query) {
        $query
            ->where('title', 'LIKE', '%ca%')
            ->orWhere('body', 'LIKE', '%ca%');
    })
    ->get();

実際に実行されるSQLは下記のようになります。

select * from `posts` where `author_user_id` = 3 and (`title` LIKE "%ca%" or `title` LIKE "%ca%");

リレーション先のテーブルでwhere条件設定

リレーションしたテーブルのカラムに対して条件を設定して検索します。
下記のサンプルでは、ユーザー名に一致する記事を取得します。リレーション先のテーブルのカラムに対して条件を設定したい場合は、EloquentのwhereHasを利用します。

$user = User::inRandomOrder()->first();

$posts = Post::with(['user'])
    ->whereHas('user', function ($query) use ($user) {
        $query->where('name', $user->name);
    })
    ->get();

実際に実行されるSQLは下記のようになります。
whereHasを利用すると、サブクエリにより実行されます。

select 
  * 
from 
  `posts` 
where 
  exists (
    select 
      * 
    from 
      `users` 
    where 
      `posts`.`author_user_id` = `users`.`id` 
      and `name` = "三宅 くみ子"
  );

複数のタグに関連する記事の取得

最後に、こちらもよくあるケースで、任意の2つのタグの両方に属する記事を取得したいケースです。
同様に、EloquentのwhereHasを利用して実装し、下記のようになります。

$tags = Tag::inRandomOrder()->take(2)->get();

$posts = Post::with(['tags'])
    ->where(function ($query) use ($tags) {
        foreach ($tags as $tag) {
            $query->whereHas('tags', function ($query) use ($tag) {
                $query->where('tag_id', $tag->id);
            });
        }
    })
    ->get();

実際に実行されるSQLは下記のようになります。

select 
  * 
from 
  `posts` 
where 
  (
    exists (
      select 
        * 
      from 
        `tags` 
        inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id` 
      where 
        `posts`.`id` = `post_tag`.`post_id` 
        and `tag_id` = 1
    ) 
    and exists (
      select 
        * 
      from 
        `tags` 
        inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id` 
      where 
        `posts`.`id` = `post_tag`.`post_id` 
        and `tag_id` = 2
    )
  )

最後に: LaravelのWhereについて

今回のLaravelのwhereの使い方についてまとめると、

  • LaravelのEloquentはとても直感的に利用することができる
  • whereは関数を渡すこともでき、柔軟に様々な検索条件を設定することができる
  • whereHasの条件はサブクエリで発行され、パフォーマンスが出ない可能性はある

となります。

Laravelで実行されたSQLと実行時間は、DBファザードで確認することが出来ますので、処理に時間がかかる場合は実際に実行されるSQLを確認してみると良いと思います。

この記事と関連している記事