контроллер представления модели — запуск функций вставки в PHP как транзакции

У меня есть класс Model, который находится в Model.php, и у меня есть две модели, такие как ModelOne и ModelTwo в ModelOne.php и ModelTwo.php. Эти модели расширяют базовый класс 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 */

Я хочу, чтобы эти две функции хранилища (каждая из которых запускала «prepare», «bindValue» и «execute» из PDO) выполнялись как транзакции. Что я должен делать? Я предпочитаю не менять свою структуру MVC.

Редактировать:
$anotherParam переменная добавлена.

1

Решение

Без изменения вашего текущего DatabaseConnector и класс наследования

Вы можете организовать «транзакционный» запасной вариант, используя пару try...catch блоки, если storeX() а также 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 воплощать в жизнь синглтон.

Я не думаю, что ваш 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

Другие решения

@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, чтобы предложить общедоступный обработчик базы данных для поддержки транзакций.

Ответ Нердвуда состоит в том, чтобы сделать это транзакцией на уровне языка, мой ответ — способ, которым база данных обрабатывает саму транзакцию.

Кроме того, вам следует проверить ядро ​​базы данных, поддерживает ли оно транзакции.

1

По вопросам рекламы [email protected]