[CakePHP] 実行前の生SQL文を取得する

  • このエントリーをはてなブックマークに追加

プレースホルダを使ったSQLは実行後のクエリログを参照すれば実際に流したSQLの全文が取得できますが、実行前はそうもいきません。
さて、こういう時どうするかと考えた結果です。
CakePHP3.5 & MySQLで確認。

クエリ実行前はQuery::sql()でバインド用の名前付きパラメータがついた状態のクエリが取得できますが、PDOのプリペアドステートメントを使用している都合、実行前はそのままコピペして実行できるようなパラメータ置換済みのクエリは取得できません。
ちなみに実行後は公式のドキュメントの通りログに出力できるので、ロガーに引っ掛ければどうにかなると思います。

データベースの基本 - 3.x

じゃあ実行前はどうなのかと、調べてもやっぱ無理っぽいので以下の通り自力で置換しました。
今のところこれで足りてますが、合ってるか分からないので使いたい方はよく注意して使ってください(^_^.)

use Cake\ORM\Query;

// ...

/**
 * @param Query $query
 * @return string
 */
public function storedSql(Query $query)
{
    $conn = $query->getConnection();
    $sql = $query->sql();
    $params = $query->getValueBinder()->bindings();

    $values = array_map(function ($param) {
        switch($param['type']){    
            case 'integer':
                return "{$param['value']}";

            case 'boolean':
                return $param['value'] ? 'TRUE' : 'FALSE';

            case 'NULL':
                return 'NULL';

            case 'string':
            default:
                return sprintf("'%s'", $conn->quote($param['value']));
        }
    }, $params);

    return strtr($sql, $values);
}

$param['type']に何も入ってない場合の条件がよくわからないので、MySQLのゆるふわ解釈に委ねる!(;^ω^)
で、これをどこに置くかは使い方次第だと思うので、お好きなところにこのメソッドを追加してください。
あとは見ての通りCake\ORM\Queryを引数に渡せばなんとかなるので、下記のような使い方ができます。

$query = $this->Posts->query()
    ->insert()
    ->values([
        'title' => 'PostTitle',
        'body' => 'Body of the post',
    ]);

echo $this->storedSql($query);
// ex. INSERT INTO posts (title, body) VALUES('PostTitle', 'Body of the post')

長いクエリも1行で全部出力されるので、これをダンプして見たい時は別途でフォーマッターを使うといいかもしれませんね。

jdorn/sql-formatter: A lightweight php class for formatting sql statements. Handles automatic indentation and syntax highlighting.

$ composer require jdorn/sql-formatter
use SqlFormatter;

// ...

echo SqlFormatter::format($this->storedSql($query), false);

2018-01-16 追記

文字列のエスケープ処理が入っていなかったので追加しました。

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。




コメントを残す