モデルのqueryメソッドでfind、findAllと同等の返り値を得る方法

常識かもしれませんが、明言されている記事が見つからなかったのでエントリー。

動作確認バージョンは毎度のBeta 1.2.0.6311です。

queryとfind、findAllでの返り値の違い

例えばfindAllとqueryのそれぞれを使って全件取得した結果の違いを見比べてみます。(DBMSMySQLを使用)

まずfindAllを使う場合。

$this->User->findAll();

の結果は

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => aaa
                    [password] => xxx
                )
        )
    [1] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => bbb
                    [password] => yyy
                )
        )
)

となります。


次にqueryを使った場合。

$this->User->query('select * from users');

の結果は

Array
(
    [0] => Array
        (
            [users] => Array
                (
                    [id] => 1
                    [username] => aaa
                    [password] => xxx
                )
        )
    [1] => Array
        (
            [users] => Array
                (
                    [id] => 2
                    [username] => bbb
                    [password] => yyy
                )
        )
)

となります。

両者の違いは連想配列のkeyが'User'か'users'で異なる部分です。

このままでも使えることは使えますが、使用する側(コントローラやビュー)で配列のkeyが'User'と'users'で混在するのはシステムで統一感が無く、少し気持ち悪いです。

そこで次の方法を使えば、queryメソッドの場合も配列のkeyを'User'にすることが出来ます。

findAllと同じ返り値を得る方法

以下のようにテーブルに別名を付ける、その別名が配列のkeyとなります。

$this->User->query('select * from users as User');

とすると、

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => aaa
                    [password] => xxx
                )
        )
    [1] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => bbb
                    [password] => yyy
                )
        )
)

の結果が返ってきます。


デバッグモードで表示されるSQLクエリを見ながら色々試してみた結果、この方法を見つけました。

結合させても大丈夫

結合させるテーブルにも別名をつけておくとアソシエーションを使った場合と同等の返り値を得ることが出来ます。

$this->User->query('select * from users as User left join posts as Post on Post.user_id = User.id');

の結果は

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => aaa
                    [password] => xxx
                )
            [Post] => Array
                (
                    [id] => 1
                    [user_id] => 1
                    [title] => title1
                    [body] => hogehoge
                )
        )
    [1] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => bbb
                    [password] => yyy
                )
            [Post] => Array
                (
                    [id] => 2
                    [user_id] => 2
                    [title] => title2
                    [body] => hogehoge
                )
        )
)

となります。


ちなみにPostgreSQLでは

少し試した感じではMySQLほど簡単には出来ないようでした。

PostgreSQLでqueryメソッドを使った場合、

$this->User->query('select * from users');

の結果は

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [id] => 1
                    [username] => aaa
                    [password] => xxx
                )
        )
    [1] => Array
        (
            [0] => Array
                (
                    [id] => 2
                    [username] => bbb
                    [password] => yyy
                )
        )
)

と配列のkeyに'users'すら入りません。


次にMySQLではうまくいったテーブル名の別名をつけて、

$this->User->query('select * from users as "User"');

としても結果は変わらず。


色々試した結果、

$this->User->query('select id as "User__id", username as "User__username", password as "User__password" FROM users')

という風にフィールド名の別名として

keyにしたい文字 + __(アンダーバー2つ) + 列名

をセットすると、

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [id] => 1
                    [username] => aaa
                    [password] => xxx
                )
        )
    [1] => Array
        (
            [User] => Array
                (
                    [id] => 2
                    [username] => bbb
                    [password] => yyy
                )
        )
)

という期待する値が返ってきました。


取得したいフィールド全てに別名を付けていかないと駄目なようです。


使いどころ

queryメソッド自体、あまり出番が少ないかもしれませんが、

    • 複雑なSQLの組み立てが必要なためにqueryメソッドを使う場合
    • SQLチューニングによるパフォーマンス改善のためにfind系メソッドからqueryメソッドに置き換える場合

などの際にこの方法を知っておくと、コントローラーやビューで返り値を今までどおり扱えて便利かも知れません。