フレームワークのクエリビルダでQueryを作って、データを取り出して実行!としたときに、期待した結果が得られないのは日常茶飯事です。そんなとき原因調査の一つとして、実際に実行されたSQLの確認することがあります。
今回はそんなときに使える「実行したQueryのオブジェクトからSQLを生成」する方法です。
SQLを取り出す(ただしバインド前)
cakephpのAPIリファレンスを眺めていると、sql()という、まんまなメソッドが見つかります。
さあこれで一件落着、とはいきません。なぜなら、このメソッドで出力されるのは各種値がバインドされる前のSQLとなります。例えば以下のような感じです。
SELECT * FROM tab WHERE id=:c0
WHEREなどに指定した値が「:c0」のようなプレースホルダになっていて、実際に実行したSQLそのものではありません。
バインドを取り出す
ならば、ということでバインドのデータを取り出します。getValueBinder()というメソッドになります。さらに続けて、**bindings()**というメソッドを実行すればバインドの情報を取得できます。バインドの情報は以下のような配列でした。
$bind = $query->getValueBinder()->bindings();
/* bindの中身
[
":c0" => [
"value" => "11",
"type" => null,
"placeholder" => "c0"
]
]
*/
キーがプレースホルダ、valueが置き換える値になっているようです。
バインドを適応する
やり方はいろいろあると思いますが、例えば以下です。
function toSql($query)
{
$sql = $query->sql();
$bind = $query->getValueBinder()->bindings();
// プレースホルダを値で文字列置換。値はクォートで囲むこと。
foreach($bind as $param => $value) {
$sql = mb_ereg_replace($param, "'{$value["value"]}'", $sql);
}
return $sql;
}
こんな関数を用意しておけばQueryのオブジェクトを渡すだけでSQLを取得できます。
おわりに
デバッグコンソールからアクセスできるようなところにこんな関数を定義しておくと、Query実行後あたりでブレークしてその場で呼び出し、実行したSQLを確認できるので便利です。
余談ですが、laravelでも同じ流れでSQLを構築できます。メソッドがそれぞれtoSql()、getBindings()になります。