| この記事の要点 |
|
やりたいこと: 括弧付きの WHERE を書く
「colA か colB が val1 で、かつ colC が val2」という条件を、素の SQL で書くと次のようになります。
SELECT * FROM article
WHERE (colA = 'val1' OR colB = 'val1') AND colC = 'val2'
これをクエリビルダ(または Eloquent)で書くと次のとおりです。
Article::where(function ($query) {
$query->where('colA', 'val1')
->orWhere('colB', 'val1');
})
->where('colC', 'val2')
->get();
where() の引数にクロージャ(無名関数)を渡すと、その中で組み立てた条件全体が括弧でくくられます。DB::table('article') で始めるクエリビルダでも書き方は同じです。
なぜネストが必要なのか(AND と OR の優先順位)
SQL では AND の方が OR より先に評価されます。ネストせずに次のように書くと、意図と違う SQL になります。
// 間違い: 括弧が付かない
Article::where('colA', 'val1')
->orWhere('colB', 'val1')
->where('colC', 'val2')
->get();
-- 生成される SQL
WHERE colA = 'val1' OR colB = 'val1' AND colC = 'val2'
-- 実際の解釈
WHERE colA = 'val1' OR (colB = 'val1' AND colC = 'val2')
この場合、colA が val1 の行は colC の値に関係なくすべて取得されてしまいます。エラーにはならず「件数が多い」「削除済みのデータまで出る」といった形で表面化するため、気づきにくいバグになります。orWhere を使うときは、まずネストが必要かを考えるのが安全です。
よく使うパターン
キーワード検索(外側の変数を使う)
クロージャの中から外側の変数を参照するには use で渡します。PHP 7.4 以降ならアロー関数でも書けます。
$keyword = $request->input('q');
// use で渡す
Article::where('status', 'published')
->where(function ($query) use ($keyword) {
$query->where('title', 'like', "%{$keyword}%")
->orWhere('body', 'like', "%{$keyword}%");
})
->get();
// アロー関数(外側の変数を自動で参照できる)
Article::where('status', 'published')
->where(fn ($q) => $q->where('title', 'like', "%{$keyword}%")
->orWhere('body', 'like', "%{$keyword}%"))
->get();
WHERE status = 'published' AND (title LIKE '%...%' OR body LIKE '%...%')
OR の中に AND のグループを作る
orWhere() にもクロージャを渡せます。
Article::where('category_id', 1)
->orWhere(function ($query) {
$query->where('category_id', 2)
->where('is_featured', true);
})
->get();
WHERE category_id = 1 OR (category_id = 2 AND is_featured = 1)
2 段以上の入れ子
クロージャの中でさらにクロージャを渡せば、何段でもネストできます。
Article::where(function ($q) {
$q->where('colA', 'val1')
->orWhere(function ($q2) {
$q2->where('colB', 'val1')
->where('colD', '>', 10);
});
})
->where('colC', 'val2')
->get();
WHERE (colA = 'val1' OR (colB = 'val1' AND colD > 10)) AND colC = 'val2'
条件があるときだけネストを追加する
検索フォームのように入力の有無で条件を変える場合は when() と組み合わせます。
Article::query()
->when($keyword, function ($query, $keyword) {
$query->where(function ($q) use ($keyword) {
$q->where('title', 'like', "%{$keyword}%")
->orWhere('body', 'like', "%{$keyword}%");
});
})
->where('colC', 'val2')
->get();
Laravel 11 などの新しいバージョンでは、「複数カラムのどれかが条件に一致」を whereAny(['title', 'body'], 'like', "%{$keyword}%") と短く書けるメソッドもあります。使っているバージョンの公式ドキュメントで提供有無を確認してください。
生成される SQL の確認方法
意図どおり括弧が付いているかは、get() の代わりに次のメソッドで確認できます。
// プレースホルダ (?) 付きの SQL
$sql = Article::where(...)->where('colC', 'val2')->toSql();
// 値を埋め込んだ SQL(Laravel 10 系後半以降)
$sql = Article::where(...)->where('colC', 'val2')->toRawSql();
// その場で出力して処理を止める
Article::where(...)->where('colC', 'val2')->dd();
開発中は Laravel Debugbar や Telescope を入れておくと、実行されたすべての SQL を画面で確認できて便利です。
落とし穴
- ローカルスコープ内の orWhere: 自作のスコープ(
scopeXxx)の中で orWhere を使う場合も、スコープの中身をクロージャでくくっておかないと、呼び出し側の条件と混ざります - like 検索の値: ユーザー入力の
%や_はワイルドカードとして解釈されます。完全一致させたい場合はエスケープが必要です - インデックス: OR 条件や前方一致でない LIKE はインデックスが効きにくく、データが多いと遅くなります。
EXPLAINで実行計画を確認しましょう
関連
- SQL WHERE 句完全ガイド
- Laravel Eloquent select (取得) 完全ガイド
- Laravel Eloquent ORM 入門 — モデル定義/CRUD/リレーション/Eager Loading 総まとめ
- SQL JOIN の ON と WHERE の違い 完全ガイド
子ページはありません
- SELECT
- INSERT
- UPDATE
- DELETE
- order by句のキャスト
- count / max / average (集計)
- 配列を条件にする方法
- where句の入れ子(ネスト)
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38