FuelPHP で複数の OR 条件を使う SQL を書く

FuelPHP で複数の OR 条件を書く場合,Query Builder を使う必要があって,ORM だと実現できなかった.検証環境は FuelPHP 1.6 で,もしかしたら FuelPHP 1.7 以降で修正されているかもしれないけど,調べてる限りだとできなさそう.

前提

SQL に特に意味は無くて,単純に外側で2回 OR 条件を連結する例で考える.

Query Builder

Orm\Model

or_where_open()or_where_close() で囲って書いた.

return Model_User::query()
    ->or_where_open()
    ->where('id', '=', 1)
    ->or_where_close()
    ->or_where_open()
    ->where('id', '=', 2)
    ->where('type', '=', 2)
    ->or_where_close()
    ->or_where_open()
    ->where('id', '=', 3)
    ->where('type', '=', 3)
    ->or_where_close()
    ->from_cache(false)
    ->get();

以下の SQL が生成される.

SELECT
    *
FROM
    `users` AS `t0`
WHERE
    (
        `t0`.`id` = 1
    ) OR
    (
        `t0`.`id` = 2 AND
        `t0`.`type` = 2
    ) OR
    (
        `t0`.`id` = 3 AND
        `t0`.`type` = 3
    )

Orm\Model_Soft

Orm\Model_Soft を継承している場合,自動的に論理削除条件が入るため,1回目を and_where_open()and_where_close() で囲う必要がある.

return Model_User::query()
    ->and_where_open()
    ->where('id', '=', 1)
    ->and_where_close()
    ->or_where_open()
    ->where('id', '=', 2)
    ->where('type', '=', 2)
    ->or_where_close()
    ->or_where_open()
    ->where('id', '=', 3)
    ->where('type', '=', 3)
    ->or_where_close()
    ->from_cache(false)
    ->get();

以下の SQL が生成される.

SELECT
    *
FROM
    `users` AS `t0`
WHERE
    `t0`.`deleted_at` IS null AND
    (
        `t0`.`id` = 1
    ) OR
    (
        `t0`.`id` = 2 AND
        `t0`.`type` = 2
    ) OR
    (
        `t0`.`id` = 3 AND
        `t0`.`type` = 3
    )

ORM

ORM だと生成される SQL が期待してるものと異なってしまう.具体的には or キーが重複しているため,上書きされて最後の条件だけが残る.どうやるんだろう.調べたけどできなさそうだった.core を修正して or1or2 など拡張したキーを許容させることはできそうだけど,別に Query Builder で良いじゃん!という結論に落ち着いた.

return Model_User::find('all', array(
    'where'      => array(
        array('id', '=', 1),
        'or' => array(
            array(
                array('id', '=', 2),
                array('type', '=', 2)
            )
        ),
        'or' => array(
            array(
                array('id', '=', 3),
                array('type', '=', 3)
            )
        )
    ),
    'from_cache' => false,
));

ダメだ…!

SELECT
    *
FROM
    `users` AS `t0`
WHERE
    `t0`.`id` = 1 AND
    `t0`.`deleted_at` IS null OR
    (
        (
            (
                `t0`.`id` = 3 AND
                `t0`.`type` = 3
            )
        )
    )

関連記事