Transaction.php 4.21 KB
Newer Older
w  
Qiang Xue committed
1 2 3
<?php
/**
 * @link http://www.yiiframework.com/
Qiang Xue committed
4
 * @copyright Copyright (c) 2008 Yii Software LLC
w  
Qiang Xue committed
5 6 7
 * @license http://www.yiiframework.com/license/
 */

Qiang Xue committed
8
namespace yii\db;
w  
Qiang Xue committed
9

10
use Yii;
Qiang Xue committed
11
use yii\base\InvalidConfigException;
w  
Qiang Xue committed
12

w  
Qiang Xue committed
13
/**
w  
Qiang Xue committed
14 15
 * Transaction represents a DB transaction.
 *
Qiang Xue committed
16
 * It is usually created by calling [[Connection::beginTransaction()]].
w  
Qiang Xue committed
17
 *
w  
Qiang Xue committed
18 19
 * The following code is a typical example of using transactions (note that some
 * DBMS may not support transactions):
w  
Qiang Xue committed
20
 *
w  
Qiang Xue committed
21 22 23
 * ~~~
 * $transaction = $connection->beginTransaction();
 * try {
24 25 26 27
 *     $connection->createCommand($sql1)->execute();
 *     $connection->createCommand($sql2)->execute();
 *     //.... other SQL executions
 *     $transaction->commit();
28
 * } catch (Exception $e) {
29
 *     $transaction->rollBack();
w  
Qiang Xue committed
30
 * }
w  
Qiang Xue committed
31
 * ~~~
w  
Qiang Xue committed
32
 *
33
 * @property boolean $isActive Whether this transaction is active. Only an active transaction can [[commit()]]
34
 * or [[rollBack()]]. This property is read-only.
35
 *
w  
Qiang Xue committed
36
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
37
 * @since 2.0
w  
Qiang Xue committed
38
 */
Qiang Xue committed
39
class Transaction extends \yii\base\Object
w  
Qiang Xue committed
40
{
41 42 43 44 45 46 47 48 49 50 51 52
    /**
     * @var Connection the database connection that this transaction is associated with.
     */
    public $db;
    /**
     * @var integer the nesting level of the transaction. 0 means the outermost level.
     */
    private $_level = 0;

    /**
     * Returns a value indicating whether this transaction is active.
     * @return boolean whether this transaction is active. Only an active transaction
53
     * can [[commit()]] or [[rollBack()]].
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
     */
    public function getIsActive()
    {
        return $this->_level > 0 && $this->db && $this->db->isActive;
    }

    /**
     * Begins a transaction.
     * @throws InvalidConfigException if [[db]] is `null`.
     */
    public function begin()
    {
        if ($this->db === null) {
            throw new InvalidConfigException('Transaction::db must be set.');
        }
        $this->db->open();

        if ($this->_level == 0) {
            Yii::trace('Begin transaction', __METHOD__);
            $this->db->pdo->beginTransaction();
            $this->_level = 1;

            return;
        }

        $schema = $this->db->getSchema();
        if ($schema->supportsSavepoint()) {
            Yii::trace('Set savepoint ' . $this->_level, __METHOD__);
            $schema->createSavepoint('LEVEL' . $this->_level);
        } else {
            Yii::info('Transaction not started: nested transaction not supported', __METHOD__);
        }
        $this->_level++;
    }

    /**
     * Commits a transaction.
     * @throws Exception if the transaction is not active
     */
    public function commit()
    {
        if (!$this->getIsActive()) {
            throw new Exception('Failed to commit transaction: transaction was inactive.');
        }

        $this->_level--;
        if ($this->_level == 0) {
            Yii::trace('Commit transaction', __METHOD__);
            $this->db->pdo->commit();

            return;
        }

        $schema = $this->db->getSchema();
        if ($schema->supportsSavepoint()) {
            Yii::trace('Release savepoint ' . $this->_level, __METHOD__);
            $schema->releaseSavepoint('LEVEL' . $this->_level);
        } else {
            Yii::info('Transaction not committed: nested transaction not supported', __METHOD__);
        }
    }

    /**
     * Rolls back a transaction.
     * @throws Exception if the transaction is not active
     */
    public function rollBack()
    {
        if (!$this->getIsActive()) {
            throw new Exception('Failed to roll back transaction: transaction was inactive.');
        }

        $this->_level--;
        if ($this->_level == 0) {
            Yii::trace('Roll back transaction', __METHOD__);
            $this->db->pdo->rollBack();

            return;
        }

        $schema = $this->db->getSchema();
        if ($schema->supportsSavepoint()) {
            Yii::trace('Roll back to savepoint ' . $this->_level, __METHOD__);
            $schema->rollBackSavepoint('LEVEL' . $this->_level);
        } else {
            Yii::info('Transaction not rolled back: nested transaction not supported', __METHOD__);
            // throw an exception to fail the outer transaction
            throw new Exception('Roll back failed: nested transaction not supported.');
        }
    }
w  
Qiang Xue committed
144
}