10 Транзакции модели
Когда процесс выполняет несколько операций в базе данных, очень важно, чтобы каждый шаг в этой группе выполнился успешно, тем самым поддерживая целостность данных. Транзакции предоставляют возможность реализации такого подхода, когда группа операций с базой данных может быть выполнена либо целиком и успешно, соблюдая целостность данных, либо не выполнена вообще.
Транзакции в Phalcon позволяют зафиксировать результат всех операции, если они были успешно выполнены, или откатить все операции, если хоть что-то пошло не так.
Ручные транзакции
Если приложение использует только одно соединение с базой данных и транзакции не очень сложны, транзакция может быть создана просто переводом текущего соединения в режим транзакции, и система делает откат или фиксацию, в зависимости от того, операция успешна или нет:
<?php use Phalcon\Mvc\Controller; class RobotsController extends Controller { public function saveAction() { // Запуск транзакции $this->db->begin(); $robot = new Robots(); $robot->name = 'WALL-E'; $robot->created_at = date('Y-m-d'); // Не удалось сохранить модель, поэтому откатываем транзакцию if ($robot->save() === false) { $this->db->rollback(); return; } $robotPart = new RobotParts(); $robotPart->robots_id = $robot->id; $robotPart->type = 'head'; // Не удалось сохранить модель, поэтому откатываем транзакцию if ($robotPart->save() === false) { $this->db->rollback(); return; } // Фиксация транзакции $this->db->commit(); } }
Неявные транзакции
Существующие отношения (связи) между таблицами могут быть использованы для хранения записей и связанных с ними моделей. Этот вид операций неявно создает транзакцию, чтобы удостовериться, что данные сохраняются правильно:
<?php $robotPart = new RobotParts(); $robotPart->type = 'head'; $robot = new Robots(); $robot->name = 'WALL-E'; $robot->created_at = date('Y-m-d'); $robot->robotPart = $robotPart; // Создает неявную транзакцию, чтобы сохранить обе записи $robot->save();
Изолированные транзакции
Изолированные транзакции выполняются в новом соединении, гарантируя, что все сгенерированные SQL-запросы, проверки виртуальных внешних ключей и бизнес логика изолированы от основного соединения. Этот вид транзакции требует менеджера транзакций, который в свою очередь, глобально управляет каждой транзакции, гарантируя правильные откат/фиксацию операций перед окончанием запроса:
<?php use Phalcon\Mvc\Model\Transaction\Failed as TxFailed; use Phalcon\Mvc\Model\Transaction\Manager as TxManager; try { // Создаём менеджера транзакций $manager = new TxManager(); // Запрос транзакции $transaction = $manager->get(); $robot = new Robots(); $robot->setTransaction($transaction); $robot->name = 'WALL·E'; $robot->created_at = date('Y-m-d'); if ($robot->save() === false) { $transaction->rollback( 'Невозможно сохранить робота' ); } $robotPart = new RobotParts(); $robotPart->setTransaction($transaction); $robotPart->robots_id = $robot->id; $robotPart->type = 'head'; if ($robotPart->save() === false) { $transaction->rollback( 'Невозможно сохранить часть робота' ); } // Всё прошло хорошо, фиксация $transaction->commit(); } catch (TxFailed $e) { echo 'Не удалось, причина: ', $e->getMessage(); }
Транзакции могут быть использованы для удаления нескольких записей на постоянной основе:
<?php use Phalcon\Mvc\Model\Transaction\Failed as TxFailed; use Phalcon\Mvc\Model\Transaction\Manager as TxManager; try { // Создаём менеджера транзакций $manager = new TxManager(); // Запрос транзакции $transaction = $manager->get(); // Получить роботов для удаления $robots = Robots::find( "type = 'mechanical'" ); foreach ($robots as $robot) { $robot->setTransaction($transaction); // Что-то идёт не так, мы должны откатить транзакцию if ($robot->delete() === false) { $messages = $robot->getMessages(); foreach ($messages as $message) { $transaction->rollback( $message->getMessage() ); } } } // Всё прошло хорошо, давайте зафиксируем транзакцию $transaction->commit(); echo 'Роботы успешно удалены!'; } catch (TxFailed $e) { echo 'Не удалось, причина: ', $e->getMessage(); }
Транзакция продолжается, независимо от того, где получается объект транзакции. Новая транзакция формируется только при выполнении методов commit()
или rollback()
. Вы можете воспользоваться DI-контейнером, чтобы создать общий менеджер транзакций для всего приложения:
<?php use Phalcon\Mvc\Model\Transaction\Manager as TransactionManager; $di->setShared( 'transactions', function () { return new TransactionManager(); } );
Тогда доступ к нему из контроллера или представления может быть осуществлён следующим образом:
<?php use Phalcon\Mvc\Controller; class ProductsController extends Controller { public function saveAction() { // Получить TransactionsManager из DI-контейнера $manager = $this->di->getTransactions(); // Или $manager = $this->transactions; // Запрос транзакции $transaction = $manager->get(); // ... } }
Пока транзакция активна, менеджер транзакций всегда будет возвращать одну и ту же транзакцию.