Difference between #3 and #4 of
How to nest DB transactions without actually nesting them.

Changes

Title unchanged

How to nest DB transactions without actually nesting them.

Category unchanged

How-tos

Yii version unchanged

Tags unchanged

transaction, transactions, db, begintransaction, rollback, commit

Content changed

[...]
```php
class YActiveRecord extends CActiveRecord {
/**
* Keeps transaction status

 
     *   
(true if previous transaction, transaction if
 
     *
local trasaction, null if no transaction). * * @var CDbTransaction|| bool|| null */ private $_transaction=null;
 
/** * Begin a transaction on the database. */ public function beginTransaction() {         if(YII_DEBUG) {
 
            Yii::trace("Begin transaction");
 
        }
 
if($this->_transaction= !== null) {
throw new CException('Transaction already started');
}
[...]
*/
public function commit() {
if(
$this->_transaction===null) {
 
            throw new CException('No ongoing transaction');
 
        } else {
 
            $this->_transaction->commit();
 
        }
 
        $this->_
YII_DEBUG) {
 
            Yii::trace("Commit 
transaction=null"); }
 
 
    /**
 
     * Rollback a transaction on the database.
 
     * @throws CException
 
     */
 
    public function rollback() {
    }
 
if($this->_transaction === null) { throw new CException('No ongoing transaction'); } else if($this->_transaction!==true){ $this->_transaction->rollbackcommit(); } $this->_transaction=null; } }
 
```
 
 
So to use the transaction replacements, you have to extend your models from YActiveRecord in stead of CActiveRecord.   You can also choose to put the above code in your models of course.
 
 
As I found it cumbersome to do a 'getDbConnection()' first and then do the transaction request, I have made those "native" methods of the ActiveRecord.  You are either doing a transaction with the model or not, you do not really want to know about the database.
 
 
The implemention will check if a transaction is already ongoing on the database or not.  If there is one, no new transaction will be created and '$_transaction' is set to true to indicate that a transaction is active, but that it was not created in this instance.
 
It relies on the fact that you should begin and end a transaction in the same method.  It does not allow you to end a transaction in another model (while other implementations allow you to do that, which might actually be error prone).
 
 
When ending the transaction ('commit' or 'rollback'), this implementation will check if this instance previously started a transaction, and then actually perform the 'commit' or 'rollback' depending on whether the real transaction was started locally or not.
 
 
The risk of this implementation is that a nested transaction that you 'rollback' is not actually rolled back in the database.  This method relies on the fact that the calling method will check the result of the "sub"-method and rollback itself if an error is returned.
 
 
Hence you should use it like this:
 
 
 
 
 
 
```php 
class Model1 extends YActiveRecord {
 
   public function complexRecordCreation() {
 
       $result=true; // Suppose everything is ok.
 
       $this->beginT

 
    /**
 
     * Rollback a transaction on the database.
 
     *
 
     * @throws CException
 
     */
 
    public function rollback() {
 
        if(YII_DEBUG) {
 
            Yii::trace("Rollback transaction");
 
        }
 
        if($this->_transaction === null) {
 
            throw new CException('No ongoing t
ransaction('); // Do some work. ($result&=...)
 
       $result&=$model2->complexRecordCreation();
 
 
       // ...
 
       if($result)
 
           $result&=$model3->save();
 
       // Do some other work. ($result&=...)
 
       if(!$result) {
 
          $this->rollback();
 
       } else {
 
          $this->commit();
 
       }
 
       return $result;
 
   }
 
}
 
 
class Model2 extends YActiveRecord {
 
   public function complexRecordCreation() {
 
       $result=true; // Suppose everything is ok.
 
 } elseif($this->_transaction!==true){
 
     
$this->beginT_transaction();
 
       // Do some work. ($result&=...)
 
       $result&=$model4->complexRecordCreation();
 
 
       // Do some other work. ($result&=...)
 
 
       if(!$result) {
 
          $this
->rollback(); } else {
 
  
 }
 
$this->commit();
 
       }
 
       return $res
_transaction=nultl;   }
}
```

In the above code, '$result' indicates the success of the operation. You can use this to check if you should continue performing the operation or not in the complex operation - in the end it must indicate the overall success of the operation.
As the callers rely on this result, the overall transaction is rolled back even if one of the subtransactions fails.
[...]
1 0
1 follower
Viewed: 11 042 times
Version: 1.1
Category: How-tos
Written by: le_top
Last updated by: le_top
Created on: Nov 22, 2014
Last updated: 10 years ago
Update Article

Revisions

View all history