@Hellek
Люблю говорить и слышать суть

Как решить проблему с PDO transaction. Не работает Rollback?

Все таблицы - InnoDB.

Вставка данных производится последовательно в 3 разных таблицы. Сначала в таблицу объектов (построек), получаем айдишник, создаем клиента в другой таблице, получаем айдишник, в итоге создаем заказ, в котором указываем айдишники объекта и клиента.

Таблицы "'obje !!! cts" не существует. Если убрать 2 Insert для клиента и заказа, то прилетает Exception, который и говорит, что такой таблицы не существует. Как бы тут откат и не нужен, раз таблицы нет, то вставка в норм таблицу не произвелась.

Сценарий второй. 2 insert'ы приведенные в примере остаются на месте. Ошибка в названии первой таблицы присутствует. Блок catch срабатывает, т.к. $result возвращает по прежнему ту же ошибку, но не срабатывает rollback. В таблицу клиентов и заказов данные вставляются. И условно когда id'шники в заказах должны были быть как (189, 189, 189), по факту оказывается как (189, 189, 0) т.к. lastID от ошибочной таблицы возвращает false.

Я может недополнял, но по идее всё же должно откатиться, если в блоке try произошла ошибка? Нужно обязательно записать 3 таблицы и в такой последовательности. Без возвращенных адекватных айдишников таблицы не связать.

function create(array $data)
{
	try {
		// Начинаем транзакцию
		$this->DBH->beginTransaction();

		// Исполняем запросы
			// Объект
			$objectInsertId = $this->insert([
				'thisTransactionOff' => 1,
				'table' => 'obje  !!!  cts',
				'data' => [
					'oObject' => $data['objects']['oObject'],
					'oAddress' => $data['objects']['oAddress'],
					'oFloorArea' => $data['objects']['oFloorArea'],
					'oWallArea' => $data['objects']['oWallArea']
				]
			]);

			// Клиент
			$personInsertId = $this->insert([
				'thisTransactionOff' => 1,
				'table' => 'persons',
				'data' => [
					'object_id' => $objectInsertId,
					'pName' => $data['persons']['pName'],
					'pPhone' => $data['persons']['pPhone'],
					'pEmail' => $data['persons']['pEmail'],
					'pSource' => $data['persons']['pSource'],
					'pRegistrationDate' => $today
				]
			]);

			// Заказ
			$orderInsertId = $this->insert([
				'thisTransactionOff' => 1,
				'table' => 'orders',
				'data' => [
					'person_id' => $personInsertId,
					'object_id' => $objectInsertId,
					'ordRecallTime' => $data['orders']['ordRecallTime'],
					'ordMeasureTime' => $data['orders']['ordMeasureTime'],
					'ordRegistration' => $today,
					'ordStatus' => $data['orders']['ordStatus'],
					'ordCreator' => $_SESSION['user_id'],
					'ordManager' => $_SESSION['user_id']
				]
			]);


		// Запрос исполнен без очевидных ошибок, фиксируем изменения
		$result = $orderInsertId;
		$this->DBH->commit();

	} catch (Exception $e) {
		// Откатываем изменения в случае ошибки
		$this->DBH->rollBack();
		$result = "Заказ не был добавлен: " . $e->getMessage();
	}

	// Возвращаем айди нового заказа или сообщение об ошибке
	return $result;
}


Содержимое метода insert
function insert(array $param)
{
	// Обязательно наличие имени таблицы и данных для вставки
	if (
			isset($param['table'])
			&& $param['table'] != ''
			&& isset($param['data'])
			&& !empty($param['data'])
		) {
		$param['ignore'] = (isset($param['ignore'])) ? 'IGNORE' : '';

		// Разбираем массив для формирования prepared statement
		foreach ($param['data'] as $field => $v) {
			$fields[] = $field;
			$values[] = ":$field";
		}
		$fields = implode(",", $fields);
		$values = implode(",", $values);

		// Формируем строку и выполняем запрос
		$query = $this->DBH->prepare("INSERT {$param['ignore']} INTO {$param['table']} ($fields) VALUE ($values)");

		// Если мы хотим использовать несколько вставок в рамках одной транзакции
		// То отключаем транзакции у этого метода и оборачиваем несколько запросов в одну новую транзакцию
		if (!isset($param['thisTransactionOff'])) {
			try {
				// Начинаем транзакцию
				$this->DBH->beginTransaction();

				// Исполняем запрос
				$query->execute($param['data']);
				$result = $this->DBH->lastInsertId();

				// Запрос исполнен без очевидных ошибок, фиксируем изменения
				$this->DBH->commit();
			} catch (Exception $e) {
				// Откатываем изменения в случае ошибки
				$this->DBH->rollBack();
				$result = "Ошибка: " . $e->getMessage();
			}
		} else {
			try {
				// Исполняем запрос
				$query->execute($param['data']);
				$result = $this->DBH->lastInsertId();
			} catch (Exception $e) {
				$result = "Ошибка: " . $e->getMessage();
			}
		}
	} else {
		$result = 'Вы не указали имя таблицы или данные для вставки';
	}

	// Возвращаем айди вставленной записи или сообщение об ошибке
	return $result;
}
  • Вопрос задан
  • 327 просмотров
Решения вопроса 1
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Во-первых, в функции insert транзакция вообще смысла не имеет, там один запрос, который выполняется атомарно.
Затем, если в функции insert возникает исключение, то вы его отлавливаете в блоке catch и возвращаете ошибку, однако в функции create вы нигде не проверяете, что именно вернулось из insert.
catch в create не сработает, поскольку исключение уже перехвачено в insert.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы