Для того, щоб розпочати нову транзакцію, необхідно просто викликати $txn = db_transaction(); у власному коді. Транзакція буде залишатися відкритою до тих пір, доки змінна $txn залишатиметься в області виконання. Коли змінна $txn буде знищена, транзакція стане виконана. Якщо ваша транзакція є вкладеністю іншої, тоді Drupal виконуватиме кожну операцію та завершить зовнішню транзакцію лише тоді, коли всі об’єкти вийдуть з області виконання, тобто всі запити пройдуть успішно.
У випадку ж, якщо один із запитів у транзакції не вдається виконати, відбувається “відкат” усіх змін, так званий “rollback” - повернення даних до вихідного стану перед початком транзакції.
Приклад:
<?php
function my_transaction_function() {
// The transaction opens here.
$transaction = db_transaction();
try {
$id = db_insert('example')
->fields(array(
'field1' => 'mystring',
'field2' => 5,
))
->execute();
my_other_function($id);
return $id;
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('my_type', $e);
}
// $transaction goes out of scope here. Unless it was rolled back,
// it gets automatically commited here.
}
function my_other_function($id) {
// The transaction is still open here.
if ($id % 2 == 0) {
db_update('example')
->condition('id', $id)
->fields(array('field2' => 10))
->execute();
}
}
?>
Якщо ж з'явилася необхідність одну з транзакцій завершити передчасно, для цього потрібно “знищити” змінну $transaction за допомогою функції unset().
<?php
unset($transaction);
?>
Попрацюємо з транзакціями на прикладі.
Уявімо ситуацію, коли є необхідність опрацювати велику кількість даних для користувачів, операція для яких повинна бути згрупована. Задача: зняття та зарахування коштів із внутрішніх рахунків користувачів на основі записів у тимчасовій таблиці.
<?php
function update_users_accounts() {
// The transaction opens here.
$accounts_transaction = db_transaction();
try {
// Select all users, that should be processed.
$query = db_select(‘account_tmp_table’, 'at');
$query->fields('at', array('uid', ‘decrease’, ‘increase’));
$query->condition('at.timestamp', $time, '>');
$result = $query->execute();
while($record = $result->fetchAssoc()) {
// Remove money from user account.
decrease_users_accounts($record[‘uid’], $record[‘decrease’]);
// Add money to user account.
increase_users_accounts($record[‘uid’], $record[‘increase’]);
// Delete temp record from table.
delete_users_account_record($record[‘uid’]);
}
return TRUE;
}
catch (Exception $e) {
$accounts_transaction->rollback();
watchdog_exception('users_accounts', $e);
}
// $transaction goes out of scope here. Unless it was rolled back,
// it gets automatically commited here.
}
function decrease_users_accounts($uid, $decrease) {
// The transaction opens here.
// Get current user account.
$current_account = get_user_account($uid);
$updated_account = $current_account - $decrease;
// Update user's account record.
db_update('account_data_table')
->condition('uid', $uid)
->fields(array('account' => $updated_account))
->execute();
}
function increase_users_accounts($uid, $increase) {
// Get current user account.
$current_account = get_user_account($uid);
$updated_account = $current_account + $increase;
// Update user's account record.
db_update('account_data_table')
->condition('uid', $uid)
->fields(array('account' => $updated_account))
->execute();
}
function delete_users_account_record($uid) {
// The transaction opens here.
db_delete('account_tmp_table')
->condition('uid', $uid)
->execute();
}
}
?>
Варіант реалізації через транзакції унеможливить зміни в рахунку користувача іншим функціоналом під час виконання цього коду, а також, у випадку виникнення помилок під час роботи, забезпечить збереження цілісності виконання операції за допомогою виклику методу rollback, який поверне усі дані до початкового стану.
В даній статті розглянуто можливість підтримки транзакцій при розробці сайтів на Drupal. Для більш детального ознайомлення можна скористатись документацією