Когда процесс выполняет несколько операций в базе данных, очень важно, чтобы каждый шаг в этой группе выполнился успешно, тем самым поддерживая целостность данных. Транзакции предоставляют возможность реализации такого подхода, когда группа операций с базой данных может быть выполнена либо целиком и успешно, соблюдая целостность данных, либо не выполнена вообще.

Транзакции в 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();

        // ...
    } 
}

Пока транзакция активна, менеджер транзакций всегда будет возвращать одну и ту же транзакцию.