はじめに
こんにちは!
バックエンドエンジニアの @kat0 です。
アドベント企画に参加するのは初めてですが、いつも書いているような記事を投稿しようと思います。
環境
Laravel v9.5.1 (PHP v8.1.3)
状況
組織1の管理者権限をもったユーザー(role
がowner
かadmin
)を取得したい。
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()
おわりに
取得できる結果は同じでも、コードを見たときの理解のしやすさは他の人や未来の自分のためにも大事だと思うので、これからも頭に入れておきたい。