CS-Cartをいじっているがアーキテクチャが古いせいか、MySQLのテーブルエンジンがMyISAMであり、したがってトランザクションは使っていない。トランザクションなしで複数のテーブルを同時更新しているわけだが、ショッピングカートのプロダクトとしていいのだろうか?
せめて自分が作ったアドオンではトランザクションを使いたいので、InnoDB化したときどうやってトランザクションするかを説明したい。
Pre/Post Controllerを使ってBEGINとCOMMITを挟むようにする
Pre/Post ControllerはCS-Cartのコアのコントローラの前後に何か処理を実行する仕組みだ。詳細は、Pre- and Post- コントローラー · CS-Cart開発者ドキュメントを見ていただければと思う。
前後ということは、これを利用するとコアの処理をBEGINとCOMMITで挟むことができる。
app/addons/mymod/controllers/backend/premoderation.pre.php
if ($_SERVER['REQUEST_METHOD'] === 'POST' and $mode === 'products_approval') {
db_query('BEGIN');
}
app/addons/mymod/controllers/backend/premoderation.post.php
if ($_SERVER['REQUEST_METHOD'] === 'POST' and $mode === 'products_approval') {
// テーブルを更新する処理など
db_query('COMMIT');
}
一応、CS-Cartは更新が失敗すると例外を投げるので、PostコントローラのCOMMITには到達しない。
PHPフックを使って、BEGIN/COMMITで挟む
PHPフックが提供されているルーチンなら幸いだ。それを使うことができるし、呼び出される順番も指定できるので、ほぼ確実にCOMMITを全フックの最後にすることができる。
app/addons/mymod/init.php
fn_register_hooks(
['update_product_pre', '1'],
['update_product_post', '9999999']
);
この1や9999999は呼び出される優先度で、詳しくはCS-Cart: PHPフックで呼び出される優先度を指定する - Qiitaを参照。
app/addons/mymod/func.php
function fn_mymod_update_product_pre($product_data, $product_id, $lang_code, $can_update) {
db_query('BEGIN');
}
function fn_mymod_update_product_post($product_data, $product_id, $lang_code, $create) {
// テーブルを更新する処理など
db_query('COMMIT');
}
こうしたやり方は明らかにHackだが、コアにはできるだけ触れたくない場合はこうするしかない。