diff --git a/src/Connection.php b/src/Connection.php index b29a5c74..be3472e6 100755 --- a/src/Connection.php +++ b/src/Connection.php @@ -25,12 +25,14 @@ use Artemeon\Database\Schema\Table; use Artemeon\Database\Schema\TableIndex; use BackedEnum; +use Closure; use Generator; use InvalidArgumentException; use Override; use Psr\Log\LoggerInterface; use RuntimeException; use Stringable; +use Throwable; /** * This class handles all traffic from and to the database and takes care of a correct tx-handling @@ -741,6 +743,24 @@ public function rollBack(): void $this->numberOfOpenTransactions--; } + #[Override] + public function transactional(Closure $callback): mixed + { + $this->beginTransaction(); + + try { + $callbackResult = $callback(); + + $this->commit(); + } catch (Throwable $exception) { + $this->rollBack(); + + throw $exception; + } + + return $callbackResult; + } + /** * @inheritDoc * @throws ConnectionException diff --git a/src/DoctrineConnectionInterface.php b/src/DoctrineConnectionInterface.php index c3c51934..bb175916 100644 --- a/src/DoctrineConnectionInterface.php +++ b/src/DoctrineConnectionInterface.php @@ -13,8 +13,10 @@ namespace Artemeon\Database; +use Closure; use Generator; use Stringable; +use Throwable; /** * Interface, which is compatible to the Doctrine DBAL Connection class @@ -128,4 +130,16 @@ public function commit(): void; * Rollback of the current transaction. */ public function rollBack(): void; + + /** + * Execute a Closure within a transaction. + * + * @template TReturn of mixed + * + * @param Closure():TReturn $callback + * + * @throws Throwable + * @return TReturn + */ + public function transactional(Closure $callback): mixed; } diff --git a/src/MockConnection.php b/src/MockConnection.php index e6b728e7..565579ee 100644 --- a/src/MockConnection.php +++ b/src/MockConnection.php @@ -18,6 +18,7 @@ use Artemeon\Database\Schema\Table; use Artemeon\Database\Schema\TableIndex; use BackedEnum; +use Closure; use Generator; use Override; @@ -221,6 +222,12 @@ public function transactionRollback(): void { } + #[Override] + public function transactional(Closure $callback): mixed + { + return $callback(); + } + #[Override] public function hasDriver(string $class): bool {