2015年4月26日

【備忘録】CakePHPトランザクション処理の仕方

 CakePHPでトランザクション処理のトランザクション処理の書き方をわかりやすくするためのコンポーネントとかを書いたのに別のプロジェクトをやった時に中途半端にロジックを書いてちゃんと動かなかったので忘れないようにするために記録しておきます。

 共通部品化できているの使いまわせると思います。

 まず、アプリケーションを作る時どこでトランザクションを管理すべきかですが、やっぱりコントローラで管理するのが正しいと思っています。
 CakePHPの場合、DataSourceに対してトランザクションをかけるのですが、DataSourceはモデルが持っているので、イマイチなコーディングになっていまします。
 テーブルが1つの場合は、良いですが、複数のテーブルになった途端見た目がおかしなことになってしまいます。

テーブル1つの場合
トランザクションをかけたいテーブルが1つに明確なのでbeginの意味はわかりやすいです。
    $this->Table1->begin();

    $this->Table1->save($data);

    $this->Table1->commit();

テーブル2つの場合
Table1のbegin()を実行した時点でデータソースに対してトランザクションがかかるので、Table2もトランザクション対象になります。
でも、見た目、Table2はトランザクション対象にならない感じがします。
    $this->Table1->begin();

    $this->Table1->save($data1);
    $this->Table2->save($data2);  // ←トランザクション対象外に見える

    $this->Table1->commit();


じゃぁ、どうするか!

複数のテーブルにトランザクションをかける場合、明示的に使用するモデルを指定する方式をとります。
トランザクション用のコンポーネントとモデルにトランザクション用のメソッドの追加をします。

TransactionComponent.php
class TransactionComponent extends Component {
  /**
   * トランザクション開始
   *
   * @param array $models トランザクション処理するモデルを全て指定する
   */
  public function begin($models) {
    $this->models = $models;
    foreach ($models as $model) {
      $model->begin();
    }
  }

  /**
   * ロールバック
   */
  public function rollback() {

    foreach ($this->models as $model) {
      $model->rollback();
    }
  }

  /**
   * コミット
   */
  public function commit() {

    foreach ($this->models as $model) {
      $model->commit();
    }

  }
}
AppModel.php
モデルに対して、トランザクションの開始、commit、rollbackのメソッドを追加する。
class AppModel extends Component {
App::uses('Model', 'Model');
class AppModel extends Model {

  /**
  * トランザクション開始
  */
  public function begin() {
   $db = $this->getDataSource($this->useDbConfig);
    $db->begin();
  }

  /*
  * トランザクションコミット
  */
  public function commit() {
    $db = $this->getDataSource($this->useDbConfig);
    $db->commit();
  }

  /*
  * トランザクションロールバック
  */
  public function rollback() {
    $db = $this->getDataSource($this->useDbConfig);
    $db->rollback();
  }

}
HogeController.php
使うモデルを明示的に指定するので、わかりやすくなる
App::uses('Model', 'Model');
class HogeController extends AppController {

  public $uses = array("Table1", "Table2");
  public $components = array("Transaction");

  private function register($data) {

    try {
      $this->Transaction->begin(array("Table1", "Table2"));

      if(!$this->Table1->save($data)) {
        $this->rollback();
        return false;
      )

      if(!$this->Table2->save($data)) {
        $this->rollback();
        return false;
      )

      $this->commit();

      return true;

    }catch(Exception $e) {
      $this->rollback();
      return false;
    }

  }

}
これは、CakePHPを使い始めてDBのトランザクションはどうやって扱ったら良いかというのを調査していたらあるサイト(今もあると思いますが忘れました)で一つの解として紹介されていたもものです。  実際の所、ルールなので、複数テーブルにトランザクション処理をするときは、代表のテーブルに対してbegin、commit、rollbackをするということにすればいちいちこんなめんどくさいことをしなくても良いのですけどね。

0 件のコメント:

コメントを投稿