Laravel – ANDとORの優先順位

概要

where()orWhere()組み合わせることで、WHERE句のANDORを表現できる。ここではこれらのメソッドの書き方と生成されるSQL、条件の組み合わせの優先順位を整理する。

以下のようなメソッドの書き方が可能になる。

  • 条件1 OR 条件2 AND 条件3ANDが優先される書き方
  • (条件1 OR 条件2) AND 条件3で括弧内のORが優先される書き方

where()orWhere()については、クエリービルダーのメソッドを参照。

準備

クエリービルダーで使った以下の3つのテーブルで各メソッドを確認する。

これをordersに対してcustomersemailsを左結合で関連付けて並べると以下のとおり。

チェーンの展開とANDの優先

where()orWhere()をチェーンで連ねた場合、それぞれの条件が単純にANDあるいはORで連ねられる。その結果ANDが処理された後ORが処理される。

  • ...->where(条件)'... AND 条件'
  • ...->orWhere(条件)'... OR 条件'

以下の例では、where()->orWhere()->where()とメソッドチェーンを組んでいる。

上記のクエリービルダーで生成されるSQLは以下のとおり。

この結果AND演算が優先され、「item='wire'または、item='rubber sheet'かつid=1」という条件で2つのデータが抽出される。

  • 4: 2020-06-07 00:00:00
    • customer name: customer1
    • email address: customer1@mail.com
    • ordered item : rubber sheet
  • 5: 2020-06-08 00:00:00
    • customer name: customer3
    • email address:
    • ordered item : wire

 

クロージャによる()の表現

ANDに対してORの条件を優先させたい場合、SQLでは括弧で括る。クエリービルダーでは、括弧で括りたい処理をクロージャ―でまとめる。

where(function($query) { 括弧でまとめたい処理群; })

以下の例では、上の例と順番は同じだが、最初のwhere()orWhere()をクロージャ―でまとめている。

この結果、まず最初の2項のOR演算が処理され、その結果と残りの条件のAND演算で抽出処理される。生成されるSQLは以下のとおりでOR演算が括弧で括られている。

実行結果は以下のとおりで、「item='wire'またはitem='rubber sheet'」で3つのデータが抽出され、その結果とid=1ANDで結果が一つに絞り込まれる。

  • 4: 2020-06-07 00:00:00
    • customer name: customer1
    • email address: customer1@mail.com
    • ordered item : rubber sheet

 

 

Laravel – リレーション/クエリー

 

リレーション

  • 複数テーブルで構成されるデータを、モデルベースで扱う方法
  • モデルでhasOne()hasMany()belongsTo()メソッドによってリレーションを定義
  • 親モデルに属する子モデルを、親モデルの属性のように扱えるようになる

クエリービルダー
クエリービルダーのメソッド

  • SQLによりフレームワークではなくRDBMS側でテーブルの連結や絞り込みなどの操作を行う
  • クエリービルダーメソッドを組み合わせてSQLを生成する

ANDとORの優先順位

  • WHERE条件でANDORを組み合わせる場合の書き方
  • where()orWhere()の優先順位とクロージャ―による括弧の実装

クエリービルダーとリレーション

  • クエリービルダーの効率性と、リレーションによる直感的な操作を組み合わせる手順

子モデルの属性による絞り込み

  • 子モデルの属性の条件による絞り込みを、クエリービルダーで行う方法
  • whereHas()メソッドを使う

ローカルスコープクエリー

  • 特定モデルで利用するクエリービルダーを、モデルのメソッドとして定義する

 

Laravel – 子テーブルの条件で絞り込み

概要

複数テーブルが関連付けられているデータを子テーブルの条件で絞り込みたい場合。クエリービルダーJOIN句によってテーブルを結合すると結果は配列となるが、配列ではなくオブジェクトのままリレーションで操作したいとき。

whereHas()メソッドを使うと、親のモデルでhasMany()で定義された子モデルの操作が可能になる。

whereHas()の使い方は以下のとおり。

  • 第1引数で子モデル名を指定
  • 第2引数にクエリーを引数としたコールバックを定義
    • コールバックで引数のクエリーに対してクエリービルダーメソッドを適用
  • 最後のget()を忘れがちなので注意

準備

クエリービルダーの確認で使ったテーブルで確認する。

1つ先のリレーション

親が直接持つ子モデルの場合、以下の様に記述する。ここでは子モデルのcustomerの名前が'customer2'のデータを抽出している。

抽出結果は親モデルのコレクションとしてビューに渡している。

モデルのコレクションを受け取ったビューでは、親モデルとしてこれらを扱い、必要に応じてリレーションによって子モデルの属性を取り出す。

emailを持たないcustomerに対応するため、optional()メソッドを使っている

結果は以下のとおりで、customer.id=2に相当するcustomer2のデータが抽出されている。

  1. 2020-06-05 00:00:00: customer2
    • e-mail: customer2@mail.com
    • item: rubber sheet
  2. 2020-06-06 00:00:00: customer2
    • e-mail: customer2@mail.com
    • item: plastic plate

 

whereHas()で生成されるSQLを確認しておく。where句の対象としてサブクエリーが構成されている(見やすいように結果を改行している)。

バインドされる値も確認。

2つ先のリレーション

子モデルの子モデルの属性で絞り込む場合は、whereHas()のコールバック内でwhereHas()を入れ子で呼び出す。呼び出し部分だけを取り出すと以下のとおり。

コールバックの引数は、コールバック内ローカルスコープなので同じ名前でも構わない。

結果は以下のとおりで、メールアドレスが'customer2'で始まるcustomer2のみ取り出されている。

  1. 2020-06-05 00:00:00: customer2
    • e-mail: customer2@mail.com
    • item: rubber sheet
  2. 2020-06-06 00:00:00: customer2
    • e-mail: customer2@mail.com
    • item: plastic plate

 

生成されるSQLを確認。サブクエリーが入れ子で構成されている。

バインドされる値も確認。