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

Run insert functions in PHP as transaction

我有一个模型 class,它位于 Model.php,我有两个模型,如 ModelOne 和 ModelTwo,分别位于 ModelOne.php 和 ModelTwo.php。这些模型扩展了基础模型 class。模型 class 扩展了一个 DatabaseConnector class,它使用 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();
    }
}

我运行在我的模型中使用两个不同的函数class是这样的:

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

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

我想将这两个存储函数(PDO 中的每个 运行、"prepare"、"bindValue" 和 "execute")作为事务处理到 运行。我应该怎么办?我不想更改我的 MVC 结构。

编辑: $anotherParam 添加了变量。

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

如果 storeX()storeY() 方法 return Exception,您可以使用几个 try...catch 块安排 "transactional" 回退失败对象:

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 所建议),这将满足您的需求。

如果您 want/need 您的所有模型都使用 DatabaseConnector class 的相同实例(这将允许您使用来自 你的 ModelX class 如果你愿意的话),你可以让你的 DatabaseConnector 实现 the singleton pattern

我不认为你的 ModelX classes 应该扩展 DatabaseConnector class - 它们似乎不是更具体的数据库版本连接器。相反,我认为 ModelX classes 应该像这个例子一样使用 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

class Model {
    private $_mysql;

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

数据库连接器class

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

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()
    {
    }
}

@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 class 以提供 public 数据库处理程序来支持事务。

Nerdwood 的回答是让它成为语言级别的事务,我的回答是数据库处理事务本身的一种方式。

此外,您应该检查您的数据库引擎是否支持事务。