diff --git a/src/Adapter/AbstractAdapter.php b/src/Adapter/AbstractAdapter.php index 4a35dd2..6b84c7d 100644 --- a/src/Adapter/AbstractAdapter.php +++ b/src/Adapter/AbstractAdapter.php @@ -77,6 +77,12 @@ abstract class AbstractAdapter implements AdapterInterface */ protected bool $isTransaction = false; + /** + * Transaction depth + * @var int + */ + protected int $transactionDepth = 0; + /** * Constructor * @@ -150,6 +156,43 @@ public function isTransaction(): bool return $this->isTransaction; } + /** + * Get transaction depth + * + * @return int + */ + public function getTransactionDepth(): int + { + return $this->transactionDepth; + } + + /** + * Execute complete transaction with the DB adapter + * + * @param mixed $callable + * @param mixed $params + * @throws \Exception + * @return void + */ + public function transaction(mixed $callable, mixed $params = null): void + { + if (!($callable instanceof CallableObject)) { + $callable = new CallableObject($callable, $params); + } + + try { + $this->beginTransaction(); + $callable->call(); + $this->commit(); + } catch (\Exception $e) { + if ($this->transactionDepth == 0) { + $this->rollback(); + } else { + throw $e; + } + } + } + /** * Check is transaction is success * diff --git a/src/Adapter/AdapterInterface.php b/src/Adapter/AdapterInterface.php index 0b84550..738c381 100644 --- a/src/Adapter/AdapterInterface.php +++ b/src/Adapter/AdapterInterface.php @@ -84,6 +84,23 @@ public function rollback(): AdapterInterface; */ public function isTransaction(): bool; + /** + * Get transaction depth + * + * @return int + */ + public function getTransactionDepth(): int; + + /** + * Execute complete transaction with the DB adapter + * + * @param mixed $callable + * @param mixed $params + * @throws \Exception + * @return void + */ + public function transaction(mixed $callable, mixed $params = null): void; + /** * Check is transaction is success * diff --git a/src/Adapter/Mysql.php b/src/Adapter/Mysql.php index f195d21..a0986ba 100644 --- a/src/Adapter/Mysql.php +++ b/src/Adapter/Mysql.php @@ -121,15 +121,18 @@ public function hasOptions(): bool */ public function beginTransaction(?int $flags = null, ?string $name = null): Mysql { - if (($flags !== null) && ($name !== null)) { - $this->connection->begin_transaction($flags, $name); - } else if ($flags !== null) { - $this->connection->begin_transaction($flags); - } else { - $this->connection->begin_transaction(); - } + if ($this->transactionDepth == 0) { + if (($flags !== null) && ($name !== null)) { + $this->connection->begin_transaction($flags, $name); + } else if ($flags !== null) { + $this->connection->begin_transaction($flags); + } else { + $this->connection->begin_transaction(); + } - $this->isTransaction = true; + $this->isTransaction = true; + $this->transactionDepth++; + } return $this; } @@ -143,15 +146,19 @@ public function beginTransaction(?int $flags = null, ?string $name = null): Mysq */ public function commit(?int $flags = null, ?string $name = null): Mysql { - if (($flags !== null) && ($name !== null)) { - $this->connection->commit($flags, $name); - } else if ($flags !== null) { - $this->connection->commit($flags); - } else { - $this->connection->commit(); - } + $this->transactionDepth--; - $this->isTransaction = false; + if ($this->transactionDepth == 0) { + if (($flags !== null) && ($name !== null)) { + $this->connection->commit($flags, $name); + } else if ($flags !== null) { + $this->connection->commit($flags); + } else { + $this->connection->commit(); + } + + $this->isTransaction = false; + } return $this; } @@ -165,15 +172,19 @@ public function commit(?int $flags = null, ?string $name = null): Mysql */ public function rollback(?int $flags = null, ?string $name = null): Mysql { - if (($flags !== null) && ($name !== null)) { - $this->connection->rollback($flags, $name); - } else if ($flags !== null) { - $this->connection->rollback($flags); - } else { - $this->connection->rollback(); - } + $this->transactionDepth--; - $this->isTransaction = false; + if ($this->transactionDepth == 0) { + if (($flags !== null) && ($name !== null)) { + $this->connection->rollback($flags, $name); + } else if ($flags !== null) { + $this->connection->rollback($flags); + } else { + $this->connection->rollback(); + } + + $this->isTransaction = false; + } return $this; } diff --git a/src/Adapter/Pdo.php b/src/Adapter/Pdo.php index def979d..0f176f4 100644 --- a/src/Adapter/Pdo.php +++ b/src/Adapter/Pdo.php @@ -182,8 +182,11 @@ public function getType(): ?string */ public function beginTransaction(): Pdo { - $this->connection->beginTransaction(); - $this->isTransaction = true; + if ($this->transactionDepth == 0) { + $this->connection->beginTransaction(); + $this->isTransaction = true; + $this->transactionDepth++; + } return $this; } @@ -194,31 +197,41 @@ public function beginTransaction(): Pdo */ public function commit(): Pdo { - $this->connection->commit(); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + $this->connection->commit(); + $this->isTransaction = false; + } + return $this; } /** - * Method checks, whether the transaction is initiated. + * Rollback a transaction * - * @return bool + * @return Pdo */ - public function inTransaction(): bool + public function rollback(): Pdo { - return $this->connection->inTransaction(); + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + $this->connection->rollBack(); + $this->isTransaction = false; + } + + return $this; } /** - * Rollback a transaction + * Method checks, whether the transaction is initiated. * - * @return Pdo + * @return bool */ - public function rollback(): Pdo + public function inTransaction(): bool { - $this->connection->rollBack(); - $this->isTransaction = false; - return $this; + return $this->connection->inTransaction(); } /** diff --git a/src/Adapter/Pgsql.php b/src/Adapter/Pgsql.php index 097f84f..0f19740 100644 --- a/src/Adapter/Pgsql.php +++ b/src/Adapter/Pgsql.php @@ -153,8 +153,12 @@ public function hasOptions(): bool */ public function beginTransaction(): Pgsql { - $this->query('BEGIN TRANSACTION'); - $this->isTransaction = true; + if ($this->transactionDepth == 0) { + $this->query('BEGIN TRANSACTION'); + $this->isTransaction = true; + $this->transactionDepth++; + } + return $this; } @@ -165,8 +169,13 @@ public function beginTransaction(): Pgsql */ public function commit(): Pgsql { - $this->query('COMMIT'); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + $this->query('COMMIT'); + $this->isTransaction = false; + } + return $this; } @@ -177,8 +186,13 @@ public function commit(): Pgsql */ public function rollback(): Pgsql { - $this->query('ROLLBACK'); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + $this->query('ROLLBACK'); + $this->isTransaction = false; + } + return $this; } diff --git a/src/Adapter/Sqlite.php b/src/Adapter/Sqlite.php index b708fed..ae25fd9 100644 --- a/src/Adapter/Sqlite.php +++ b/src/Adapter/Sqlite.php @@ -134,8 +134,12 @@ public function dbFileExists(): bool */ public function beginTransaction(): Sqlite { - $this->query('BEGIN TRANSACTION'); - $this->isTransaction = true; + if ($this->transactionDepth == 0) { + $this->query('BEGIN TRANSACTION'); + $this->isTransaction = true; + $this->transactionDepth++; + } + return $this; } @@ -146,8 +150,13 @@ public function beginTransaction(): Sqlite */ public function commit(): Sqlite { - $this->query('COMMIT'); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + $this->query('COMMIT'); + $this->isTransaction = false; + } + return $this; } @@ -158,8 +167,13 @@ public function commit(): Sqlite */ public function rollback(): Sqlite { - $this->query('ROLLBACK'); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + $this->query('ROLLBACK'); + $this->isTransaction = false; + } + return $this; } diff --git a/src/Adapter/Sqlsrv.php b/src/Adapter/Sqlsrv.php index b1c33a4..02c9f19 100644 --- a/src/Adapter/Sqlsrv.php +++ b/src/Adapter/Sqlsrv.php @@ -137,8 +137,12 @@ public function hasOptions(): bool */ public function beginTransaction(): Sqlsrv { - sqlsrv_begin_transaction($this->connection); - $this->isTransaction = true; + if ($this->transactionDepth == 0) { + sqlsrv_begin_transaction($this->connection); + $this->isTransaction = true; + $this->transactionDepth++; + } + return $this; } @@ -149,8 +153,13 @@ public function beginTransaction(): Sqlsrv */ public function commit(): Sqlsrv { - sqlsrv_commit($this->connection); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + sqlsrv_commit($this->connection); + $this->isTransaction = false; + } + return $this; } @@ -161,8 +170,13 @@ public function commit(): Sqlsrv */ public function rollback(): Sqlsrv { - sqlsrv_rollback($this->connection); - $this->isTransaction = false; + $this->transactionDepth--; + + if ($this->transactionDepth == 0) { + sqlsrv_rollback($this->connection); + $this->isTransaction = false; + } + return $this; } diff --git a/src/Record.php b/src/Record.php index 01f1b52..adf7c09 100644 --- a/src/Record.php +++ b/src/Record.php @@ -29,12 +29,6 @@ class Record extends Record\AbstractRecord { - /** - * Transaction depth - * @var int - */ - protected static int $depth = 0; - /** * Constructor * @@ -197,20 +191,16 @@ public static function start(): static|null $args = func_get_args(); $class = get_called_class(); if (Db::hasDb($class)) { - if (self::$depth == 0) { + if (Db::db($class)->getTransactionDepth() == 0) { Db::db($class)->beginTransaction(); } } if ($class !== 'Pop\Db\Record') { $record = (!empty($args)) ? (new \ReflectionClass($class))->newInstanceArgs($args) : new static(); - if (self::$depth == 0) { - $record->startTransaction(); - } - self::$depth++; + $record->startTransaction(); return $record; } else { - self::$depth++; return null; } } @@ -225,10 +215,7 @@ public static function commit(): void { $class = get_called_class(); if (Db::hasDb($class)) { - self::$depth--; - if (self::$depth == 0) { - Db::db($class)->commit(); - } + Db::db($class)->commit(); } } @@ -236,6 +223,7 @@ public static function commit(): void * Rollback transaction with the DB adapter * * @param \Exception|null $exception + * @throws Exception * @return \Exception|null */ public static function rollback(\Exception $exception = null): \Exception|null @@ -243,8 +231,7 @@ public static function rollback(\Exception $exception = null): \Exception|null $class = get_called_class(); if (Db::hasDb($class)) { - self::$depth--; - if (self::$depth == 0) { + if (Db::db($class)->getTransactionDepth() == 1) { Db::db($class)->rollback(); } else { if ($exception == null) {