findで複数のORを使う場合はarrayで囲む

CakePHPでfind使うとき

SELECT * FROM t
  WHERE
    "id" = '1'
    AND 
    (("status" = '1') OR ("flg" = '1'));

というSQLが書きたければ

find('find', array(
  'conditions' => array(
    'id' => 1,
    'OR' => array(
      'status' => 1,
      'flg'       => 1,
    ),
  ),
));

みたいな感じで書くと思います。



で、今回2個のORが使いたいなと思ったんですが、
普通に書いてしまうと

find('find', array(
  'conditions' => array(
    'id' => 1,
    'OR' => array(
      'status' => 1,
      'flg'       => 1,
    ),
    'OR' => array(
      'status' => 2,
      'flg'       => 2,
    ),
  ),
));

となってしまうため前のORが後から書いたORに上書きされてしまうなと。
ということでCookbookを調べてみたらちゃんと書いてありました。

array(
   'OR' => array(
      array('Company.name' => 'Future Holdings'),
      array('Company.city' => 'CA')
   ),
   'AND' => array(
      array(
         'OR'=>array(
            array('Company.status' => 'active'),
            'NOT'=>array(
               array('Company.status'=> array('inactive', 'suspended'))
            )
         )
     )
   )
);

なるほどなるほど。ANDでくくればいいのね。上手くいきました。



でも、「今回は2個だったので良いけど3個以上だったらどうするんだろう?」と疑問に。
さっきのORと同じ問題でANDをもう一つ書くわけにはいかない。
と考えてた時に「そういえば同じフィールドの条件を渡したいときにarrayで配列にしてたな」と思い出し、
もう一度Cookbookを参照。

 array(
    'OR' => array(
	array('Post.title LIKE' => '%one%'),
	array('Post.title LIKE' => '%two%')
    )
);

ありました。ありました。連想配列にせず、普通の配列でも渡せるようです。



ってことで、以下のようにすればORを複数にして渡せました。

find('find', array(
  'conditions' => array(
    'id' => 1,
    array('OR' => array(
      'status' => 1,
      'flg'       => 1,
    )),
    array('OR' => array(
      'status' => 2,
      'flg'       => 2,
    )),
    array('OR' => array(
      'status' => 3,
      'flg'       => 3,
    )),
  ),
));

上のfindは下のようなSQLになります。

SELECT * FROM t
  WHERE
    "id" = '1'
    AND
    (("status" = '1') OR ("flg" = '1')) 
    AND 
    (("status" = '2') OR ("flg" = '2'))
    AND
    (("status" = '3') OR ("flg" = '3'))
    LIMIT 1