Транзакции

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

Простейший способ использовать транзакции выглядит так:

$database->beginTransaction();
try {
	$database->query('DELETE FROM articles WHERE id = ?', $id);
	$database->query('INSERT INTO audit_log', [
		'article_id' => $id,
		'action' => 'delete'
	]);
	$database->commit();
} catch (\Exception $e) {
	$database->rollBack();
	throw $e;
}

Того же результата можно добиться куда изящнее методом transaction(). Он принимает callback, который выполняется внутри транзакции. Если callback отрабатывает без исключения, транзакция автоматически фиксируется. Если возникает исключение, транзакция откатывается, а исключение распространяется дальше.

$database->transaction(function ($database) use ($id) {
	$database->query('DELETE FROM articles WHERE id = ?', $id);
	$database->query('INSERT INTO audit_log', [
		'article_id' => $id,
		'action' => 'delete'
	]);
});

Вызовы transaction() можно вкладывать друг в друга, благодаря чему легко составлять методы, каждый из которых управляет своей транзакцией. В базу данных как BEGIN/COMMIT на самом деле отправляется только самая внешняя транзакция; внутренние вызовы лишь отслеживают глубину вложенности. Вызов beginTransaction(), commit() или rollBack() вручную внутри callback метода transaction() выбрасывает LogicException.

Метод transaction() может и возвращать значения:

$count = $database->transaction(function ($database) {
	$result = $database->query('UPDATE users SET active = ?', true);
	return $result->getRowCount(); // возвращает число обновлённых записей
});