PhalconでDIへDb\Adapterを登録してtransactionを一元管理するのはできないのだと思っていたら 使い方が間違っていた。
DIへ登録する時にはshared(共有)として登録すれば呼び出し先で同じコネクションのインスタンス を使用してくれる。逆を言うとsharedしないと同じインスタンスを使用してくれないようだ。
具体的には
// 第三引数にtrueを指定
$di->set('db', function() use ($config) {
return new \Phalcon\Db\Adapter\Pdo\Postgresql(
$config->database->toArray());
}, true);
本当にトランザクションが制御できているか以下の方法で確認
DBにテーブルを用意する
CREATE TABLE name1 ( id SERIAL PRIMARY KEY, name text );テスト登録用処理を実装したモデルクラスを用意する。
class Name1 extends \Phalcon\MVC\Model
{
public $id;
public $name;
public function getSource()
{
return "name1";
}
/**
* 引数のコネクションと関数内で生成したコネクション
* でそれぞれ登録
*
*/
public static function insertTest($src_db){
$name = new \App\Mvc\Model\Name1();
$db = $name->getDI()->get("db");
for($i=0;$i<5;$i++){
$name = new \App\Mvc\Model\Name1();
$name->name = 'FFFFF' . sprintf("%05d", $i);
$name->create();
$result = $db->query("INSERT INTO name1(name) VALUES(
'" . "DDDDD" . sprintf("%05d", $i) . "')");
$resulta = $src_db->query("INSERT INTO name1(name) VALUES(
'" . "EEEEE" . sprintf("%05d", $i) . "')");
}
}
}
DIのsharedをtrueにしたものとしないものの両方で以下の処理を実行する。具体的にはコントローラー、生成したモデルのテスト用関数で登録処理が行われ、例外を意図的に 発生させてrollbackを行う。
try{
$this->db->begin();
for($i=0;$i<5;$i++){
$name = new \App\Mvc\Model\Name1();
$name->name = 'AAAAAA' . sprintf("%05d", $i);
$name->create();
$result = $this->db->query("INSERT INTO name1(name) VALUES(
'" . "BBBBB" . sprintf("%05d", $i) . "')");
}
\App\Mvc\Model\Name1::insertTest($this->db);
throw new \Exception("trnsaction test");
$this->db->commit();
}catch(Exception $e){
if($this->db->isUnderTransaction()){
$this->db->rollback();
}
throw $e;
}
sharedした場合の結果は
test=# select * from name1; id | name ----+------ (0 行)しなかった場合、インスタンスを別で取得して登録した分が残ってしまう。
test=# select * from name1; id | name ----+------------ 12 | DDDDD00000 15 | DDDDD00001 18 | DDDDD00002 21 | DDDDD00003 24 | DDDDD00004 (5 行)一元管理したい場合はsharedをする事で可能になる。一番起きそうなのは一元管理するつもりだったのに sharedしないで登録してしまいtransactionがめちゃくちゃになってしまったという事故。
connectionのインスタンスはDB回りのクラスで管理されるものと思い込んでいたのでDIでやる発想が無かった。 こんな方法もあるんだな。
0 件のコメント :
コメントを投稿