From bf716e95f5323c55cfcd08bcbabe8c702fffbfad Mon Sep 17 00:00:00 2001 From: tg666 Date: Thu, 8 Apr 2021 23:17:07 +0200 Subject: [PATCH] Transaction tracker - added interface `TransactionTrackerInterface` and implementor `TransactionTracker` - added method `RuntimeException::noActiveTransaction()` - transactions are automatically tracked byl the Tracker --- src/Bridge/Nette/config/services.neon | 7 +- src/Exception/RuntimeException.php | 8 +++ src/Transaction.php | 10 ++- src/TransactionFactory.php | 9 ++- src/TransactionTracker.php | 76 +++++++++++++++++++++ src/TransactionTrackerInterface.php | 28 ++++++++ tests/Cases/TransactionFactoryTestCase.phpt | 4 +- 7 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 src/TransactionTracker.php create mode 100644 src/TransactionTrackerInterface.php diff --git a/src/Bridge/Nette/config/services.neon b/src/Bridge/Nette/config/services.neon index 6b119bb..9aecaa9 100644 --- a/src/Bridge/Nette/config/services.neon +++ b/src/Bridge/Nette/config/services.neon @@ -3,9 +3,14 @@ services: type: SixtyEightPublishers\DoctrinePersistence\TransactionFactoryInterface factory: SixtyEightPublishers\DoctrinePersistence\TransactionFactory( @Doctrine\ORM\EntityManagerInterface, - @extension.finally_callback_queue_invoker + @extension.finally_callback_queue_invoker, + @extension.transaction_tracker ) finally_callback_queue_invoker: autowired: no type: SixtyEightPublishers\DoctrinePersistence\FinallyCallbackQueueInvoker + + transaction_tracker: + type: SixtyEightPublishers\DoctrinePersistence\TransactionTrackerInterface + factory: SixtyEightPublishers\DoctrinePersistence\TransactionTracker diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php index c123450..b21a9ba 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -26,4 +26,12 @@ public static function reservedArgumentNameUsage(string $name): self $name )); } + + /** + * @return \SixtyEightPublishers\DoctrinePersistence\Exception\RuntimeException + */ + public static function noActiveTransaction(): self + { + return new static('There is no active transaction.'); + } } diff --git a/src/Transaction.php b/src/Transaction.php index b2f7f5f..b8e0385 100644 --- a/src/Transaction.php +++ b/src/Transaction.php @@ -27,6 +27,9 @@ final class Transaction implements TransactionInterface /** @var \SixtyEightPublishers\DoctrinePersistence\FinallyCallbackQueueInvoker */ private $finallyCallbackQueueInvoker; + /** @var \SixtyEightPublishers\DoctrinePersistence\TransactionTrackerInterface */ + private $transactionTracker; + /** @var iterable */ private $arguments; @@ -48,13 +51,15 @@ final class Transaction implements TransactionInterface /** * @param \Doctrine\ORM\EntityManagerInterface $em * @param \SixtyEightPublishers\DoctrinePersistence\FinallyCallbackQueueInvoker $finallyCallbackQueueInvoker + * @param \SixtyEightPublishers\DoctrinePersistence\TransactionTrackerInterface $transactionTracker * @param callable $callback * @param iterable $arguments */ - public function __construct(EntityManagerInterface $em, FinallyCallbackQueueInvoker $finallyCallbackQueueInvoker, callable $callback, iterable $arguments = []) + public function __construct(EntityManagerInterface $em, FinallyCallbackQueueInvoker $finallyCallbackQueueInvoker, TransactionTrackerInterface $transactionTracker, callable $callback, iterable $arguments = []) { $this->em = $em; $this->finallyCallbackQueueInvoker = $finallyCallbackQueueInvoker; + $this->transactionTracker = $transactionTracker; $this->callbacks = [$callback]; $this->arguments = $arguments; } @@ -67,7 +72,7 @@ public function withArguments(iterable $arguments): TransactionInterface $callbacks = $this->callbacks; $firstCallback = array_shift($callbacks); - $transaction = new static($this->em, $this->finallyCallbackQueueInvoker, $firstCallback, $arguments); + $transaction = new static($this->em, $this->finallyCallbackQueueInvoker, $this->transactionTracker, $firstCallback, $arguments); foreach ($callbacks as $callback) { $transaction->then($callback); @@ -152,6 +157,7 @@ public function run() $this->executed = TRUE; $result = NULL; + $this->transactionTracker->track($this); $this->em->getConnection()->beginTransaction(); try { diff --git a/src/TransactionFactory.php b/src/TransactionFactory.php index 12b6661..dd01463 100644 --- a/src/TransactionFactory.php +++ b/src/TransactionFactory.php @@ -14,14 +14,19 @@ final class TransactionFactory implements TransactionFactoryInterface /** @var \SixtyEightPublishers\DoctrinePersistence\FinallyCallbackQueueInvoker */ private $finallyCallbackQueueInvoker; + /** @var \SixtyEightPublishers\DoctrinePersistence\TransactionTrackerInterface */ + private $transactionTracker; + /** * @param \Doctrine\ORM\EntityManagerInterface $em * @param \SixtyEightPublishers\DoctrinePersistence\FinallyCallbackQueueInvoker $finallyCallbackQueueInvoker + * @param \SixtyEightPublishers\DoctrinePersistence\TransactionTrackerInterface $transactionTracker */ - public function __construct(EntityManagerInterface $em, FinallyCallbackQueueInvoker $finallyCallbackQueueInvoker) + public function __construct(EntityManagerInterface $em, FinallyCallbackQueueInvoker $finallyCallbackQueueInvoker, TransactionTrackerInterface $transactionTracker) { $this->em = $em; $this->finallyCallbackQueueInvoker = $finallyCallbackQueueInvoker; + $this->transactionTracker = $transactionTracker; } /** @@ -29,6 +34,6 @@ public function __construct(EntityManagerInterface $em, FinallyCallbackQueueInvo */ public function create(callable $callback, iterable $arguments = []): TransactionInterface { - return new Transaction($this->em, $this->finallyCallbackQueueInvoker, $callback, $arguments); + return new Transaction($this->em, $this->finallyCallbackQueueInvoker, $this->transactionTracker, $callback, $arguments); } } diff --git a/src/TransactionTracker.php b/src/TransactionTracker.php new file mode 100644 index 0000000..433b4e3 --- /dev/null +++ b/src/TransactionTracker.php @@ -0,0 +1,76 @@ +transactions); + } + + /** + * {@inheritDoc} + */ + public function count(): int + { + return count($this->transactions); + } + + /** + * {@inheritDoc} + * + * @throws \SixtyEightPublishers\DoctrinePersistence\Exception\InvalidArgumentException + */ + public function track(TransactionInterface $transaction): void + { + $hash = spl_object_hash($transaction); + + if (isset($this->transactions[$hash])) { + throw new InvalidArgumentException(sprintf( + 'Transaction with hash %s is already tracked.', + $hash + )); + } + + $transaction->finally(function () use ($hash): void { + unset($this->transactions[$hash]); + }); + + $this->transactions[$hash] = $transaction; + } + + /** + * {@inheritDoc} + */ + public function hasActiveTransaction(): bool + { + return !empty($this->transactions); + } + + /** + * {@inheritDoc} + * + * @throws \SixtyEightPublishers\DoctrinePersistence\Exception\RuntimeException + */ + public function getCurrentTransaction(): TransactionInterface + { + if (empty($this->transactions)) { + throw RuntimeException::noActiveTransaction(); + } + + return end($this->transactions); + } +} diff --git a/src/TransactionTrackerInterface.php b/src/TransactionTrackerInterface.php new file mode 100644 index 0000000..1827419 --- /dev/null +++ b/src/TransactionTrackerInterface.php @@ -0,0 +1,28 @@ +create(static function () { }, []));