Как выполнить несколько запросов в транзакции
Короткий ответ
Оберните запросы в beginTransaction() и commit(), а при ошибке вызовите rollBack():
<?php
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER CHECK (balance >= 0))');
$pdo->exec('INSERT INTO accounts (balance) VALUES (500), (100)');
function transfer(PDO $pdo, int $from, int $to, int $amount): void
{
$pdo->beginTransaction();
try {
$pdo->prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?')->execute([$amount, $to]);
$pdo->prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?')->execute([$amount, $from]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
}
transfer($pdo, 1, 2, 200);
try {
transfer($pdo, 1, 2, 9999);
} catch (PDOException) {
echo "Transfer cancelled\n";
}
echo implode(', ', $pdo->query('SELECT balance FROM accounts ORDER BY id')->fetchAll(PDO::FETCH_COLUMN));Результат:
Transfer cancelled
300, 300Что произошло
Второй перевод зачислил деньги получателю, но списание нарушило ограничение таблицы. rollBack() отменил оба запроса, и балансы остались прежними: 300 и 300.
Функции из этого ответа
- PDO — Подключение к базе данных.
- PDOStatement — Подготовленный запрос и его результат.
- Throwable — Общий интерфейс всего, что можно бросить и поймать.