仙台の山奥で自転車に乗ったり転んだり

愛車の GIOS でサイクリングしたりポタリングしたり、それをブログに記録してみたり。ロードバイクや自転車や坂のことを書いてみたり。ときたまプログラムのことを忘れないようにメモってみたり。

CakePHP のモデルで任意の SQL を実行するノウハウ

業務系のバックエンドをWebアプリケーションと連携するときなど、複合主キーで CakePHP が上手く操作できなくて、効率良く操作できないものかと悩んだ際のメモです。

こんなやり方がいいのかは微妙ですが、復号主キーのテーブルを一つのモデルとして利用したいケースであれば、CakePHPの規約のとおりモデルを作成して、プロパティーに「$useTable = false」を設定していました。
で、テーブルの操作などが必要なロジックでは、SQLを直に実行して対応するほうが、規模が大きくなっても何を何処で処理しているかが分かりやすいと思っています。

で、CakePHPSQLを実行する最もシンプルな方法は↓のやりかただと思うのですが、これだと String::insert() あたりが邪魔をして意図しないSQLが実行されるケースがありました。(コード番号や日付の"HHMMSS"などで、先頭の0埋めがなくなったり)

$query  = "INSERT INTO {$this->tablePrefix}table01 "
        .     "("
        .         "field01,"
        .         "field02,"
        .         "field03,"
        .         "field04,"
        .         "field05"
        .         ""
        .     ") VALUES (?, ?, ?, ?, ?, ?, ?) ";
$result = $this->query($query, $insertValues, false);

いろいろ試してみて、ちゃんとエスケープなど考慮するのであれば、こんな実行方法がベターだと思っています。

$dataSource = $this->getDataSource();

$fieldsDefinition = array(
        'field01' => 'integer',
        'field02' => 'text',
        'field03' => 'text',
        'field04' => 'text',
        'field05' => 'integer',
    );
$reference = array(
        'field01' => $id,
        'field02' => $date,
        'field03' => $time,
        'field04' => $number,
        'field05' => self::FOO_FLAG,
    );
$fields = array();
$values = array();
foreach ($fieldsDefinition as $field => $columnType) {
    $fields[] = $field;
    $rawValue = null;
    if (isset($reference[$field])) {
        $rawValue = $reference[$field];
    }
    $values[] = $dataSource->value($rawValue, $columnType, false);
}
$tableFullName = "{$this->tablePrefix}table01";
$insertFields  = implode(', ', $fields);
$insertValues  = implode(', ', $values);

$query = array(
        'table'  => $tableFullName,
        'fields' => $insertFields,
        'values' => $insertValues,
    );
$statement = $dataSource->renderStatement('create', $query);
$result    = $dataSource->execute($statement);

あと、モデルで実行したSQLのログを細かく参照しながら開発すると、意図したSQLが実行されているかをスムーズに確認できて楽だと思います。

var_dump($this->getDataSource()->getLog());