FuelPHPのORMモデルを利用した場合のトランザクション処理について


# 本題の前におさらい・・・
FuelPHPのバージョンは1.7.3
(php oil -v)
PHP 5.5.29でお話ししてます。

## ORMモデルとは
FuelPHPにはいくつかモデルがありますよね。

* Model
* Model_Crud
* Orm\Model
* Orm\Model_Sof

この中のOrm\Modelは
テーブル間のリレーションを考慮したデータベース操作が可能なモデルクラスです。
当然設定が必要で、Ormモデルのプロパティとしてリレーションタイプを設定してあげます。

```
$_has_many：一対多のリレーション
$_belongs_to：多対一のリレーション
$_has_one：一対一のリレーション
$_many_many：多対多のリレーション
```

いまさらですが、意識せずに関連するモデルを取得してくれるなんて、素敵ですね。

# ORMモデルのトランザクションの扱いは？

クエリビルダならこうですよね
(処理が適当ですみません）

```php

$db = Database_Connection::instance();
$db->start_transaction();
try {

  $query = DB::update('users');
  $query->value('name', 'ken');
  $query->where('id', '=', '2');
  $result = $query->execute();
  
  $query2 = DB::update('users_bk');
  $query2->value('name', 'ken');
  $query2->where('id', '=', '2');
  $result2 = $query2->execute();
  
  // なにが言いたいかというと、
  // 異なるテーブルでsaveする処理のトランザクション制御ってことです
  $db->commit_transaction();
catch (\Exception $ex) {
  $db->rollback_transaction();
}
```

ORMモデルならこうです
(単純なモデルの処理です。リレーションの設定があってもちゃんとロールバックすると思います。)
全ては書きませんが
Model_Article
Model_Comment
Model_Bkcomment
がいきなり登場します（ちゃんとモデルが用意されてると思ってください）

```php:どっかのコントローラクラスにて
$db = Database_Connection::instance();
$db->start_transaction();

try {

            $article = Model_Article::forge();
            $article->title = 'test';
            $article->body = 'test';
            $article->user_id = 1;
            $article->save();

            $comment = Model_Comment::forge();
            $comment->user_id = 1;
            $comment->article_id = 1;
            $comment->body = 'test';

            $bkcomment = Model_Bkcomment::forge();
            $bkcomment->user_id = 1;
            $bkcomment->article_id = 1;
            $bkcomment->body = 'test';
            
            // commentのモデルに$_has_oneでbkcommentを設定してます
            // こんな感じ。
            // リレーションのテストなので簡単です。
            // コメントテーブルとバックアップコメントテーブルは1対１の関係
            // Model_Commentには下記が設定してあります！
            // 'bkcomment' => array(
            //   'key_from' => 'id',
            //   'model_to' => 'Model_Bkcomment',
            //   'key_to' => 'comment_id',
            //   'cascade_save' => true,
            //   'cascade_delete' => true,
            // )
            $comment->bkcomment = $bkcomment;

            $comment->save();
            
            // 例外を発生させてみる
            // throw new Exception();

    $db->commit_transaction();
} catch (Exception $e) {
    $db->rollback_transaction();
}
```

普通のモデルと同じような感じですね。素晴らしい！
ちなみにOrm\Modelのsaveメソッドの定義は

```php
public function save($cascade = null, $use_transaction = false) {}
```
第二引数にトランザクションの制御フラグがありますね。
１モデル(テーブル)だけのCRUD処理で、トランザクション制御したい場合は
このフラグをtrueにすればいいと思います。

```php
if ($use_transaction)
{
    $db = \Database_Connection::instance(static::connection(true));
    $db->start_transaction();
}
```
Orm\Modelのsaveメソッドの中に上の記述があります。
フラグがあれば、事前にセットしているコネクションがなければ新しくコネクションを生成して
トランザクションを開始するってことだと思います。
ちなみに、上で紹介してる一連の流れの中で、このフラグをtrueにしてはいけません。

StackOverflowで聞いて、回答もらったので検証してみました。
https://ja.stackoverflow.com/questions/17584/fuelphp%E3%81%AEorm%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86%E5%A0%B4%E5%90%88-%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%87%A6%E7%90%86%E3%81%AF%E5%8F%AF%E8%83%BD%E3%81%8B

### あれれ、トランザクションがきかないよぉ・・・

っという場合はテーブルのストレージエンジンを確認してください。
MyISAMだとトランザクション処理はできないですよ！

http://thinkit.co.jp/free/article/0603/10/3/