diff --git a/src/Transaction.php b/src/Transaction.php index b896592..30114ad 100644 --- a/src/Transaction.php +++ b/src/Transaction.php @@ -23,6 +23,12 @@ abstract class Transaction */ protected ?string $event = null; + /** + * @var string|null $lockTableName Specifies the name of the table to + * optionally acquire a lock on for writing. + */ + protected ?string $lockTableName = null; + /** * Performs the action. * @throws Throwable @@ -34,6 +40,25 @@ private function validateAndPerform(): self $this->perform(); return $this; } + + /** + * @throws Throwable + */ + private function lockTableIfNecessary() + { + if (!$this->lockTableName) + return; + + DB::raw("LOCK TABLES `$this->lockTableName` WRITE"); + } + + private function unlockTableIfNecessary() + { + if (!$this->lockTableName) + return; + + DB::raw("UNLOCK TABLES"); + } /** * Executes the transaction inside a database transaction context. If an @@ -46,6 +71,7 @@ public function execute(): self { try { + $this->lockTableIfNecessary(); DB::transaction(fn() => $this->validateAndPerform()); } catch(Throwable $exception) @@ -53,6 +79,10 @@ public function execute(): self $this->cleanupAfterFailure(); throw $exception; } + finally + { + $this->unlockTableIfNecessary(); + } $this->fireEvent(); return $this; } diff --git a/tests/TableLockTransactonTests.php b/tests/TableLockTransactonTests.php new file mode 100644 index 0000000..be60e28 --- /dev/null +++ b/tests/TableLockTransactonTests.php @@ -0,0 +1,43 @@ +integer('a'); + $table->integer('b'); + $table->integer('c'); + }); + } + + protected function tearDown(): void + { + Schema::drop(TableLockTransaction::TableName); + + parent::tearDown(); + } + + function testLockedTableInsertDoesNotThrow() + { + (new TableLockTransaction())->execute(); + + /** @noinspection SqlNoDataSourceInspection */ + $row = DB::selectOne('SELECT * FROM `' . TableLockTransaction::TableName . '`'); + + $this->assertEquals(1, $row->a); + $this->assertEquals(2, $row->b); + $this->assertEquals(3, $row->c); + } + } diff --git a/tests/Transactions/TableLockTransaction.php b/tests/Transactions/TableLockTransaction.php new file mode 100644 index 0000000..dda27e2 --- /dev/null +++ b/tests/Transactions/TableLockTransaction.php @@ -0,0 +1,21 @@ +lockTableName = self::TableName; + } + + protected function perform(): void + { + /** @noinspection SqlNoDataSourceInspection */ + DB::insert("INSERT INTO `$this->lockTableName` VALUES(1, 2, 3)"); + } + }