13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qiita x Code Polaris共催!女性ITエンジニアが作るアドベントカレンダーAdvent Calendar 2022

Day 7

【Laravel】whereとorWhereの併用で気をつけること

Last updated at Posted at 2022-12-06

はじめに

こんにちは!
バックエンドエンジニアの @kat0 です。
アドベント企画に参加するのは初めてですが、いつも書いているような記事を投稿しようと思います。

環境

Laravel v9.5.1 (PHP v8.1.3)

状況

組織1の管理者権限をもったユーザー(roleowneradmin)を取得したい。

OKバージョン

組織1の管理者権限をもったユーザーのみ取得できる。

User::where('role', UserRole::Owner->value)
    ->orWhere('role', UserRole::Admin->value)
    ->where('organization_id', 1)->get();

=> Illuminate\Database\Eloquent\Collection {#4690
     all: [
       App\Models\User {#4689
         id: 1,
         name: "山田太郎",
         email: "yamada_taro@gmail.com",
         organization_id: 1,
         role: 1,
       },
     ],
   }

AND検索のwhereよりOR検索のorWhereが先にきているため、そちらが優先される。
よって「roleが1か2」かつ「所属組織が1」のユーザーが取得できる。

select * from `users` where (`role` = ? or `role` = ?) and `organization_id` = ?

NGバージョン

違う組織の管理者権限をもったユーザーも取得できてしまう。
(鈴木のぶ子さんは組織2の管理者権限をもったユーザー)

User::where('organization_id', 1)
    ->where('role', UserRole::Owner->value)
    ->orWhere('role', UserRole::Admin->value)->get();

=> Illuminate\Database\Eloquent\Collection {#4700
     all: [
       App\Models\User {#4698
         id: 1,
         name: "山田太郎",
         email: "yamada_taro@gmail.com",
         organization_id: 1,
         role: 1,
       },
       App\Models\User {#4697
         id: 2,
         name: "鈴木のぶ子",
         email: "suzuki_nobuko@gmail.com",
         organization_id: 2,
         role: 2,
       },
     ],
   }

ORよりANDの方が優先されるため、「所属組織が1かつroleが1」または「(所属組織は何でもよく)roleが2」のユーザーが取得できてしまう。

where検索の順番が前後するだけで取得したいものと結果が変わってしまう…。

select * from `users` where `organization_id` = ? and `role` = ? or `role` = ?

ドキュメントには

クエリを論理的にグループ化するため、括弧内のいくつかの"where"句をグループ化したい場合があります。実際、予期外のクエリ動作を回避するため、orWhereメソッドへの呼び出しを通常は常に括弧内へグループ化する必要があります。これには、whereメソッドにクロージャを渡します。

こうすることで行数は少し長くなってしまうけど、コードをぱっとみたときにどこでグループ化してるか一目で分かるので良さそう。

User::where('organization_id', 1)->where(function ($query) {
    $query->where('role', UserRole::Owner->value)
        ->orWhere('role', UserRole::Admin->value);
})->get();

SQLをみてもORが括弧でグループ化されて、「所属組織が1」かつ「roleが1か2」のユーザーが取得できている。

select * from `users` where `organization_id` = ? and (`role` = ? or `role` = ?)

よりすっきり書くには

グループ化してもしなくても、今回の場合はwhereInを使うとよりすっきり書ける。

// パターン1
User::whereIn('role', [UserRole::Owner->value, UserRole::Admin->value])
    ->where('organization_id', 1)->get();

// パターン2
User::where('organization_id', 1)->where(function ($query) {
    $query->whereIn('role', [UserRole::Owner->value, UserRole::Admin->value]);
})->get()

おわりに

取得できる結果は同じでも、コードを見たときの理解のしやすさは他の人や未来の自分のためにも大事だと思うので、これからも頭に入れておきたい。

13
10
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
13
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?