Как стать автором
Обновить

Магия транзакций в Bitrix: Как заставить ваши данные держаться как вкопанные

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров3.2K

? Привет! Сегодня мы поговорим о том, как использовать транзакции в Bitrix, чтобы обеспечить целостность данных. А также, что такое вложенные транзакции и для чего они нужны.


? Тайны Транзакций

Версия main 22.200.0 принесла с собой нечто волшебное - вложенные транзакции в Bitrix! Зачем они нужны? Давайте посмотрим на этот процесс с точки зрения магии.

Untitled

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

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

? Для чего нужны транзакции?

  • Транзакции позволяют объединять несколько запросов в единую конструкцию. Она гарантирует, что запросы будут выполнены как единое целое.

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

? Как это было раньше?

Прежде чем волшебство транзакций появилось в Bitrix, разработчики вынуждены были прибегать к тайным приемам.

Создание таблицы-шпиона, где каждое изменение регистрировалось. Это позволяло восстановить предыдущее состояние при ошибке.

Введение транзакций в Bitrix значительно улучшило этот процесс, предоставив более надежные и эффективные способы гарантировать целостность данных.

? Волшебство в действии: Как это работает в коде?

<?php

$application = \Bitrix\Main\Application::getInstance();
$connection = $application->getConnection();

try {
    $connection->startTransaction();
  
    // Ваш волшебный код здесь...
  
    $connection->commitTransaction();
} catch (\Throwable $e) {
    $connection->rollbackTransaction();
    throw $e;
}

Окей, давайте рассмотрим пример использования транзакций на практике. Допустим, у нас есть сущность, назовем ее ExampleTable. Мы хотим изменить поле NAME у записи с кодом 'example'. При этом, мы также хотим добавить новую запись в другую таблицу - AnotherTable.
В этом кусочке кода транзакция начинается, операции выполняются, и если что-то идет не так, мы можем легко и безопасно откатить все изменения.

<?php

$application = \Bitrix\Main\Application::getInstance();
$connection = $application->getConnection(); // Получаем соединение с базой данных

try {
    // Начинаем транзакцию
    $connection->startTransaction();

    // Получаем из сущности некий объект
    $object = \Example\ExampleTable::query()
        ->where('CODE', 'example')
        ->addSelect('NAME')
        ->fetchObject();
		
    if ($object) {
        $object->set('NAME', 'Пример записи'); // Меняем поле `NAME` у объекта
        $result = $object->save(); // Сохраняем
        if (!$result->isSuccess()) {
            throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');
        }
            
        // Пробуем добавить второй элемент в другую таблицу
        $anotherObject = \Example\AnotherTable::createObject();
        $anotherObject->set('TITLE', 'New title');
        $result = $anotherObject->save(); // Сохраняем
        if (!$result->isSuccess()) {
            // В случае ошибки откатываем транзакцию, и первый элемент не сохранится
            throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');
        }

        // Успешно завершаем транзакцию
        $connection->commitTransaction();
    } else {
        throw new \Bitrix\Main\SystemException('Объект не найден.');
    }
} catch (\Throwable $e) {
    // Если произошла ошибка, отменяем транзакцию
    $connection->rollbackTransaction();
    throw $e;
}

Волшебство становится еще увлекательнее с появлением вложенных транзакций. Давайте еще усложним пример, который покажет, как они работают.

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

<?php

$application = \Bitrix\Main\Application::getInstance();
$connection = $application->getConnection();

try {
    // Начинаем транзакцию
    $connection->startTransaction();

    // Получаем из сущности некий объект
    $object = \Example\ExampleTable::query()
        ->where('CODE', 'example')
        ->addSelect('NAME')
        ->fetchObject();
		
    if ($object) {
        $object->set('NAME', 'Пример записи'); // Меняем поле `NAME` у объекта
        $result = $object->save(); // Сохраняем
        if (!$result->isSuccess()) {
            throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');
        }
            
        // Пробуем добавить второй элемент в другую таблицу
        $anotherObject = \Example\AnotherTable::createObject();
        $anotherObject->set('TITLE', 'New title');
        $result = $anotherObject->save(); // Сохраняем
        if (!$result->isSuccess()) {
            // В случае ошибки откатываем транзакцию, и первый элемент не сохранится
            throw new \Bitrix\Main\SystemException('Ошибка в процессе сохранения данных.');
        }

        // Вложенная транзакция
        $connection->startTransaction();

        // Создаем объект для вложенной таблицы
        $nestedObject = \Example\NestedTable::createObject();
        $nestedObject->set('DESCRIPTION', 'Nested description');
        $result = $nestedObject->save();
        if (!$result->isSuccess()) {
            // В случае ошибки откатываем вложенную транзакцию 
            // Т.к у битрикса действует парадигма "вложенные роллбеки не поддерживаются" 
            // Происходит исключение \Bitrix\Main\DB\TransactionException
            $connection->rollbackTransaction();
        } else {
            // Успешно завершаем вложенную транзакцию
            $connection->commitTransaction();
        }
        
        // Успешно завершаем основную транзакцию
        $connection->commitTransaction();
    } else {
        throw new \Bitrix\Main\SystemException('Объект не найден.');
    }
} catch (\Bitrix\Main\DB\TransactionException $e) {
    // ROLLBACK вложенной транзакции
    // Тут мы можем решить, что делать, если вложенная транзакция не прошла
} catch (\Throwable $e) {
    // Если произошла ошибка, отменяем транзакцию
    $connection->rollbackTransaction();
    throw $e;
}

? Что делают методы управления транзакциями?

  • $connection->startTransaction();: Этот метод начинает транзакцию, позволяя группировать несколько операций в единое целое.

  • $connection->commitTransaction();: Вызов этого метода фиксирует все изменения, сделанные в рамках транзакции, и отправляет их в базу данных.

  • $connection->rollbackTransaction();: Если произошла ошибка или необходимо отменить все изменения в рамках транзакции, этот метод откатывает все операции.

?️‍?️ Дополнительные тайны:

  • Транзакции применимы не только к save(), но и к другим методам ORM, таким как update(), add(). А также, к SQL запросам через $connection->query()

  • Если хотите углубиться в тайны магии транзакций, загляните в официальную документацию Bitrix

? Завершение:

Таким образом, мы научились использовать волшебство транзакций в Bitrix, обеспечивая целостность данных. Если у вас есть свои волшебные методы или вопросы, делитесь в комментариях!

Теги:
Хабы:
Всего голосов 6: ↑4 и ↓2+6
Комментарии9

Публикации

Истории

Работа

PHP программист
128 вакансий

Ближайшие события