addArgument('mode', InputArgument::OPTIONAL, 'Use fail to test rollback.', 'commit'); } protected function execute(InputInterface $input, OutputInterface $output): int { $mode = (string) $input->getArgument('mode'); if (!in_array($mode, ['commit', 'fail'], true)) { $output->writeln('Use "commit" or "fail" as the mode.'); return Command::INVALID; } $this->connection->executeStatement('CREATE TABLE IF NOT EXISTS transaction_audit ( label VARCHAR(80) NOT NULL )'); $this->connection->executeStatement('DELETE FROM transaction_audit'); try { $this->connection->transactional(function (Connection $connection) use ($mode): void { $connection->executeStatement( 'INSERT INTO transaction_audit (label) VALUES (?)', ['order_reserved'], ); if ($mode === 'fail') { throw new RuntimeException('Simulated failure inside the transaction.'); } $connection->executeStatement( 'INSERT INTO transaction_audit (label) VALUES (?)', ['email_queued'], ); }); $output->writeln('Committed transaction.'); } catch (RuntimeException $exception) { $output->writeln('Rolled back transaction: '.$exception->getMessage()); } $count = (int) $this->connection->fetchOne('SELECT COUNT(*) FROM transaction_audit'); $output->writeln(sprintf('Rows in transaction_audit: %d', $count)); return Command::SUCCESS; } }