「条件句(conditional clause)」はクエリ文の中で、その結果を一定の条件にマッチする行だけに制限する部分です。SQLでは、セレクト文、アップデート文、もしくはデリート文における WHERE 句、HAVING 句がそれにあたります。Drupal のダイナミッククエリでは、それらは同じメカニズムで実装されています。ここで説明することはすべて、特別な場合を除き、次の3つのクエリタイプのすべてに適用されます。
条件フラグメントはそれで1つの条件となる部分です。
条件句はすべて、少なくとも1つ以上の条件フラグメントの組み合わせで構成されています。接続詞は具体的には「 AND 」と「 OR 」で、2つの条件文を組み合わせるのに使用されます。
Drupal はそれぞれの条件フラグメントを QueryConditional クラスのインスタンスとして表現します。それを条件オブジェクトといいます。
例えば、次のクエリは以下のように分解されます。
セレクト、アップデート、デリートのクエリオブジェクトは QueryConditionInterface の実装によって同じインターフェースを提供されています。内部的にはそれは DatabaseCondition オブジェクトを保持しています。DatabaseCondition クラスは直接インスタンス化することが出来ます。
条件文の中のそれぞれの条件フラグメントは接続詞によって連結されます。条件オブジェクトは指定の接続詞によって連結される1つ以上の条件フラグメントで構成されます。デフォルトでは接続詞は「 AND 」です。これはオプションで変更できます。条件ステートメントを構築するために、条件フラグメントをネスト(入れ子)の構造で持つことがあります。このようなしくみで、どのような複雑な条件ステートメントも淡々と構築されます。
条件オブジェクトを追加するメソッドには次の2つがあます。:
$query->condition($field, $value = NULL, $operator = '=')
condition() メソッドは $field 、$value 、$operator の様式からなる一般的な条件フラグメントを追加します。これはフィールドとその値のあらゆる比較条件( =,<,>=, LIKE など)を含みます。$operator が省略されている場合は '=' が使われます。最も一般的なケースで、たとえば condition('myfield', $value) といったメソッドでは、'myfield = :value' といった結果となり、':value' はクエリが実行される時点で $value に置き換えられます。
$query->where($snippet, $args = array())
where() メソッドは、条件フラグメントとして使われるSQL文を追加します。$snippet は追加する条件フラグメントです。変数を含む場合はプレースホルダを使用します。$args はプレースホルダと、それらを置き換える変数の配列です。開発者はスニペットに渡すSQL文が常に有効なものであることを意識する必要があります。データベース固有の変更などは、この場合は行われません。
フィールド操作(カラム操作)やフィールド同士の比較など、複雑な処理のために$field $value $operator フォーマットが当てはまらない場合以外は、多くの場合 condition() メソッドを使ったやり方のほうが好まれるでしょう。いづれも条件オブジェクトを返し、メソッドチェーンの記述でいくらでも条件の追加が可能です。
condition() メソッドは、他のいくつかの特殊なケースも処理します。
演算子には値のパラメータに配列が使われることを想定したものがあります。「 IN 」と「 BETWEEN 」です。「 IN 」は配列にフィールドと等しい値が含まれているかを評価します。たとえば次のようになります。
<?php $query->condition('myfield', array(1, 2, 3), 'IN'); // 結果: myfield IN (:db_placeholder_1, :db_placeholder_2, :db_placeholder_3) ?>
「 BETWEEN 」の場合は、フィールドの値が $value の2つの値の間であるかどうかを評価します。たとえば次のようになります。
<?php $query->condition('myfield', array(5, 10), 'BETWEEN'); // 結果: myfield BETWEEN :db_placeholder_1 AND :db_placeholder_2 ?>
condition() の1つめのパラメータには、他の条件オブジェクトを渡すことができます。こうすることで、括弧でくくられた中に入れ子の条件を記述するような形になります。入れ子のオブジェクトは親オブジェクトとは異なる接続詞を使用することもできます。このようにして、複雑な入れ子の条件オブジェクトを、ボトムアップ式に構築することが出来るわけです。
db_condition() のヘルパー関数は新しい条件オブジェクトを返します。それらは1つのパラメータとして、自身が使用する接続詞を受け取ります。db_and() 、db_or() 、db_xor() これらのヘルパー関数を使うことによって、どのような状況にも対応出来るでしょう。これによって条件オブジェクトは、非常にコンパクトな構文のクエリで内部に挿入されます。
たとえば、次のような構成を考えてみます。:
<?php $query ->condition('field1', array(1, 2), 'IN') ->condition(db_or()->condition('field2', 5)->condition('field3', 6)) // 結果: // (field1 IN (:db_placeholder_1, :db_placeholder_2) AND (field2 = :db_placeholder3 OR field3 = :db_placeholder_4)) ?>
フィールドの値がヌルかどうかを評価したい場合があります。そのような場合でも condition() メソッドは使えます。次のような実用的なメソッドを使うと直感的にも分かりやすく、好ましい記述となります。:
<?php $query->isNull('myfield'); // 結果 (myfield IS NULL) $query->isNotNull('myfield'); // 結果 (myfield IS NOT NULL) ?>
これらのメソッドはメソッドチェーンが可能です。condition() や where() と組み合わせて使うことも出来ます。
condition() メソッドは引数としてサブクエリもサポートします。サブクエリを使うには、まず db_select() メソッドで SelectQuery オブジェクトを作成します。次に、それを実行せずにそのまま、親となる他のクエリの condition() メソッドの引数として渡します。これでサブクエリは最終的な実行時に親クエリに統合されます。
サブクエリは一般的には次の2つのケースに使用できます。1つはサブクエリが1つの行の1つの値を返す場合で演算子が = 、 < 、>、<=、>= のいづれかの場合。もう1つはサブクエリが1つのカラムを返し演算子が IN の場合です。それ以外の多くの組み合わせはシンタックスエラーとなります。
※注意: 現在ではサブクエリの使用は演算子が IN の場合のみ可能となっています。このほかの操作では、サブクエリが括弧でラップされないため、シンタックスエラーとなります。これについては #1267508: Subselects don't work in DBTNG conditions, except when used as value for IN をご覧ください。
いくつかのデータベース、特に MYQL では、条件句のサブクエリはあまり高速ではありません。可能であれば、サブクエリの代わりにJOIN 構文、FROM 句でのサブクエリの使用、複数のフラットな条件フラグメントの組み合わせなどを使うようにしてください。
以下の例は条件句の構築の理解をより明確にしてくれるはずです。分かりやすくする為に、ここではプレースホルダとプリペアドステートメントのことは置いておいて、同等のクエリ文字列を一緒に表示しておきます。
<?php db_delete('sessions') ->condition('timestamp', REQUEST_TIME - $lifetime, '<') ->execute(); // DELETE FROM {sessions} WHERE (timestamp < 1228713473) ?>
<?php db_update('sessions') ->fields(array( 'sid' => session_id() )) ->condition('sid', $old_session_id) ->execute(); // UPDATE {sessions} SET sid = 'abcde' WHERE (sid = 'fghij'); ?>
<?php // From taxonomy_term_save(): $or = db_or()->condition('tid1', 5)->condition('tid2', 6); db_delete('term_relation')->condition($or)->execute(); // DELETE FROM {term_relation} WHERE ((tid1 = 5 OR tid2 = 6)) ?>