QA@IT

【CakePHP】Model::query()でプレースホルダーが機能しない

6271 PV

Model::query()でプレースホルダーによるSQL内文字列の置換を行おうとした際に、問題が発生しました。
当初はだいぶ複雑なSQLで試していたので、SQL側に問題があるのかと思いましたが、内容を削っていっても解決しなかったので、どなたかご意見いただければと思います。

環境はCakePHP2.4.7です。
WindowsにXAMPPを入れてApacheを動かし、MySQLは使わずに別途インストールしたMariaDBを使っています。
なお、cakeディレクトリのみ最新の2.4.9に置き換えた状態でもテストしてみましたが、問題は解消しませんでした。

任意のコントローラーに適当なアクションを作って、以下を実行すると、問題が発生します(前提として、productsテーブル、およびProductモデルが存在するものとします)。

$this->loadModel('Product');
$sql = "SELECT `Product`.`id` FROM `products` AS `Product` WHERE `Product`.`id` = ? LIMIT ?";
$r = $this->Product->query($sql, array(100, 1), false);
pr($r);die;

結果:

  • Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1'' at line 1

  • SQL Query: SELECT `Product`.`id` FROM `products` AS `Product` WHERE `Product`.`id` = ? LIMIT ?

不思議なことに、上掲のSQLから「 LIMIT ?」を削除し、Model::query()の第2引数を「array(100)」と書き直すと、問題は発生しません。つまり、以下の内容なら正常に結果を取得できています。

$this->loadModel('Product');
$sql = "SELECT `Product`.`id` FROM `products` AS `Product` WHERE `Product`.`id` = ?";
$r = $this->Product->query($sql, array(100), false);
pr($r);die;

この挙動はどのように考えればよいのでしょうか?
検証方法など、何かご示唆いただければと思います。

どうぞよろしくお願いします。

回答

CakePHP は詳しくないですが、そのやり方だと LIMIT に指定した値が文字列でバインドされていると思います(LIMIT '1' となる)。LIMIT には整数しか指定できないためエラーになります。

生の PDO なら bindValue で PDO::PARAM_INT を指定すれば大丈夫かもしれませんが CakePHP でバインドパラメータの型を明示的に指定する方法はわかりません。

編集 履歴 (0)
  • ご回答ありがとうございます。
    元のSQL文から「`Product`.`id` = ?」を消し、「LIMIT ?」を残す形にしてみたところ、たしかにエラーになりました。

    どうやらquery()でバインドする値については、文字列型に限ると思った方がよさそうですね。
    参考にしたページの文例ではLIMITも置換されていたので、過去にはできたのかもしれませんが。

    ありがとうございました。
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。