[PHP][Yii] ActiveQueryのandWhereとandFilterWhereの違いを整理してみた

YiiFrameworkはPHPフレームワークのひとつです。Laravelと比べて知名度や人気は控えめですが、日本語ドキュメントも揃っていて高速にWebシステムを構築するにも適しているフレームワークのひとつです。 お仕事でこのYiiを使う機会があったので、少しずつブログにしていければと思います。
2019.12.26

まいどです

CX事業本部の中安です。

今回の記事は自分の備忘録として、YiiFramework の ActiveQueryandWhereandFilterWhere の違いを簡単にまとめます。

ActiveQuery上の定義

andWhereandFilterWhere 両メソッドは、クエリの条件(Where)文を連想配列によって追加するメソッドになります。

まずはこれが何処に定義されているかですが、下記のように 親クラスのQueryが実装するQueryTrait上に存在しています。

Query → QueryTrait
↑
ActiveQuery

両メソッドのドキュメントはこちらになります。

説明の違い

andWhere()の説明はこちらになります。

Adds an additional WHERE condition to the existing one. The new condition and the existing one will be joined using the `AND` operator.

すでに存在している条件に追加のWHERE条件を足します。新しい条件は既存の条件と「AND」演算子を用いて結合されます。

続いてandFilterWhere()の説明です。

Adds an additional WHERE condition to the existing one but ignores empty operands. The new condition and the existing one will be joined using the 'AND' operator.

This method is similar to andWhere(). The main difference is that this method will remove empty query operands. As a result, this method is best suited for building query conditions based on filter values entered by users.

すでに存在している条件に追加のWHERE条件を足しますが、空のオペランドは無視します。新しい条件は既存の条件と「AND」演算子を用いて結合されます。

このメソッドは andWhere() に似ています。主な違いは空のクエリオペランドは削除するということです。その結果、このメソッドはユーザによって入力されたフィルタ値に基づいてクエリ条件を構築するのに最適です。

両メソッドとも説明はすごく似ていますが、一箇所違いがありますね。

太字で書いている「空のオペランドは無視する」というのが大きな違いのようです。 では「空のオペランド」とはどういうことでしょうか。

これは同じく QueryTraitに定義されている isEmpty というメソッドに影響されるようです。

isEmptyの説明を見てみると

Returns a value indicating whether the give value is "empty".
The value is considered "empty", if one of the following conditions is satisfied:

- it is null
- an empty string
- a string containing only whitespace characters
- or an empty array

指定した値が「空」かどうかを示す値を返します。
次の条件のいずれかが満たされている場合、値は「空」とみなされます。

- NULL
- 空の文字列
- 空白文字のみを含む文字列
- または空の配列

この条件に当てはまると Where文として追加されないということです。

PHP標準のempty関数とは仕様が違うので注意が必要そうです。

挙動を比べてみる

ActiveQueryオブジェクトを呼び出して、直接コマンドオブジェクトを作って生のSQL文を出力してみます。

$query = User::find()
    ->andWhere(['name' => 'なかやす'])
    ->andFilterWhere(['name' => 'なかやす']);

echo $query->createCommand()->rawSql;

これを実行すると

SELECT 
  *
FROM 
  `user`
WHERE 
  (`name`='なかやす') 
  AND 
  (`name`='なかやす')

(※注意: 本来は出力は改行されませんが、読みやすいように改行しています)

同じ条件が2つ並んでしまいますが、期待値としては正解です。

では、空文字列を条件にしてみます。

$query = User::find()
    ->andWhere(['name' => ''])
    ->andFilterWhere(['name' => '']);

echo $query->createCommand()->rawSql;

これを実行すると

SELECT 
  *
FROM 
  `user`
WHERE 
  (`name`='なかやす')

条件が1つだけになります。andFilterWhereに与えた条件が空文字だったので無視されたわけです。

まとめ

  • andFilterWhereandWhere と違って、条件の値が NULL、空文字列(空白スペースだけも空文字列扱い)、空配列の場合は無視をする
  • PHP標準のempryとは異なり、0falseは「空」扱いではない
  • 非必須入力項目で空文字チェックをする必要がないので便利だが、仕様を間違って把握しているとバグの温床になりやすいの注意

といったところでしょうか。

どなたかの役に立てば幸いでございます。