У меня есть класс 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
переменная добавлена.
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()
{
}
}
@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, чтобы предложить общедоступный обработчик базы данных для поддержки транзакций.
Ответ Нердвуда состоит в том, чтобы сделать это транзакцией на уровне языка, мой ответ — способ, которым база данных обрабатывает саму транзакцию.
Кроме того, вам следует проверить ядро базы данных, поддерживает ли оно транзакции.