在PHP中作为事务运行插入函数

时间:2015-10-11 10:19:19

标签: php model-view-controller pdo transactional

我有一个Model类,它在Model.php中,我有两个模型,比如ModelOne.php和ModelTwo.php中的ModelOne和ModelTwo。这些模型扩展了基础Model类。模型类扩展了一个DatabaseConnector类,它使用PDO与数据库进行交互。

class ModelOne extends Model {
    public function storeX($params1) {
        $query = 'insert ...';
        return $this->insert($query, $params1);
    }
}

class ModelTwo extends Model {
    public function storeY($params2) {
        $query = 'insert ...';
        return $this->insert($query, $params2);
    }
}

class Model extends DatabaseConnector {/* not related to this question */}

class DatabaseConnector {
    private $_mysql;

    public function __construct() {
        $this->_mysql = new PDO(...);
    }

    public function insert($query, $params = array()) {
        $sql_stmt = $this->_mysql->prepare($query);

        foreach($params as $i => $param) {
            $sql_stmt->bindValue(++$i, $param['value'], $param['type']);
        }

        $sql_stmt->execute();
        return $this->_mysql->lastInsertId();
    }
}

我在我的模型类中运行两个不同的函数,如下所示:

/* begin transaction */
$model1 = new ModelOne();
$anotherParam = $model1->storeX($params1);

$model2 = new ModelTwo();
$model2->storeY($params2, $anotherParam);
/* end transaction */

我想要这两个存储函数(每个函数从PDO运行“prepare”,“bindValue”和“execute”)作为事务运行。我该怎么办?我不想改变我的MVC结构。

编辑: $anotherParam变量已添加。

2 个答案:

答案 0 :(得分:1)

不改变您当前的DatabaseConnector和类继承

如果try...catchstoreX()方法在失败时返回storeY()个对象,则可以使用几个Exception块来安排“事务”回退:

try {
    $model1 = new ModelOne();
    $anotherParam = $model1->storeX($params1);
} catch (Exception ex) {
    //First INSERT failed
    //Handle the issue and don't continue
    exit;
}

try {
    $model2 = new ModelTwo();
    $model2->storeY($params2, $anotherParam);
} catch (Exception ex) {
    //Second INSERT failed
    //Roll back the first INSERT!
    $model1->removeX($anotherParam);
}

修改DatabaseConnector的使用方式

使用正确的MySQL数据库端事务会更好,但这需要您拥有在各种ModelX对象之间共享的单一数据库连接

您应该扩展DatabaseConnector以便很好地处理交易(正如@jayxhj所建议的那样),这将满足您的需求。

如果您希望/需要所有模型使用DatabaseConnector类的相同实例(这将允许您使用您的ModelX类中的事务,如果你想要的,你可以制作DatabaseConnector工具the singleton pattern

我认为您的ModelX类不应该扩展DatabaseConnector类 - 它们似乎不是数据库连接器的更具体版本。相反,我认为ModelX类应该像这个例子那样使用DatabaseConnector

包含交易中的来电

//Start transaction
$db = DatabaseConnector::getInstance();
$db->beginTransaction();

try
{
    //Do database work
    $model1 = new ModelOne();
    $anotherParam = $model1->storeX($params1);
    $model2 = new ModelTwo();
    $model2->storeY($params2, $anotherParam);

    //Commit the whole transaction
    $db->commit();    
}
catch (Exception ex)
{
    //Roll back the whole transaction
    $db->rollback();
}

模型类

class Model {
    private $_mysql;

    public function __construct() {
        //Get "singleton" instance of the DatabaseConnector (shared between all Models)
        $this->_mysql = DatabaseConnector::getInstance();
    }
}

DatabaseConnector类

class DatabaseConnector extends Singleton {
    private $_mysql;

    public function __construct() {
        $this->_mysql = new PDO(...);
    }

    public function beginTransaction() { 
        return $this->_mysql->beginTransaction();
    }

    public function commit() { 
        return $this->_mysql->commit();
    }

    public function rollback() { 
        return $this->_mysql->rollback();
    }
}

单身人士课程

class Singleton
{
    /**
     * @var Singleton The reference to *Singleton* instance of this class
     */
    private static $instance;

    /**
     * Returns the *Singleton* instance of this class.
     *
     * @return Singleton The *Singleton* instance.
     */
    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    /**
     * Protected constructor to prevent creating a new instance of the
     * *Singleton* via the `new` operator from outside of this class.
     */
    protected function __construct()
    {
    }

    /**
     * Private clone method to prevent cloning of the instance of the
     * *Singleton* instance.
     *
     * @return void
     */
    private function __clone()
    {
    }

    /**
     * Private unserialize method to prevent unserializing of the *Singleton*
     * instance.
     *
     * @return void
     */
    private function __wakeup()
    {
    }
}

答案 1 :(得分:1)

@Nerdwood 的答案符合您的需求。

还有另一种方式

try {
    $pdo = new PDO(...);
    $pdo->beginTransaction();
    $model1 = new ModelOne();
    $anotherParam = $model1->storeX($params1);
    $model2 = new ModelTwo();
    $model2->storeY($params2, $anotherParam);
    $pdo->commit();
} 
catch (Exception $e) {          
    $pdo->rollback();
}

但您应修改DatabaseConnector类以提供公共数据库处理程序以支持事务。

Nerdwood的答案是将其作为语言级别的交易,我的答案是数据库处理交易本身的一种方式。

此外,如果数据库引擎支持事务,则应检查数据库引擎。