Skip to content

Commit

Permalink
Transaction tracker
Browse files Browse the repository at this point in the history
- added interface `TransactionTrackerInterface` and implementor `TransactionTracker`
- added method `RuntimeException::noActiveTransaction()`
- transactions are automatically tracked byl the Tracker
  • Loading branch information
tg666 committed Apr 8, 2021
1 parent 1576251 commit bf716e9
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 6 deletions.
7 changes: 6 additions & 1 deletion src/Bridge/Nette/config/services.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions src/Exception/RuntimeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
}
}
10 changes: 8 additions & 2 deletions src/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -152,6 +157,7 @@ public function run()
$this->executed = TRUE;
$result = NULL;

$this->transactionTracker->track($this);
$this->em->getConnection()->beginTransaction();

try {
Expand Down
9 changes: 7 additions & 2 deletions src/TransactionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,26 @@ 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;
}

/**
* {@inheritDoc}
*/
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);
}
}
76 changes: 76 additions & 0 deletions src/TransactionTracker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\DoctrinePersistence;

use ArrayIterator;
use SixtyEightPublishers\DoctrinePersistence\Exception\RuntimeException;
use SixtyEightPublishers\DoctrinePersistence\Exception\InvalidArgumentException;

final class TransactionTracker implements TransactionTrackerInterface
{
/** @var \SixtyEightPublishers\DoctrinePersistence\TransactionInterface[] */
private $transactions = [];

/**
* {@inheritDoc}
*/
public function getIterator(): ArrayIterator
{
return new ArrayIterator($this->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);
}
}
28 changes: 28 additions & 0 deletions src/TransactionTrackerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\DoctrinePersistence;

use Countable;
use IteratorAggregate;

interface TransactionTrackerInterface extends IteratorAggregate, Countable
{
/**
* @internal
*
* @param \SixtyEightPublishers\DoctrinePersistence\TransactionInterface $transaction
*/
public function track(TransactionInterface $transaction): void;

/**
* @return bool
*/
public function hasActiveTransaction(): bool;

/**
* @return \SixtyEightPublishers\DoctrinePersistence\TransactionInterface
*/
public function getCurrentTransaction(): TransactionInterface;
}
4 changes: 3 additions & 1 deletion tests/Cases/TransactionFactoryTestCase.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use Tester\Assert;
use Tester\TestCase;
use Doctrine\ORM\EntityManagerInterface;
use SixtyEightPublishers\DoctrinePersistence\TransactionFactory;
use SixtyEightPublishers\DoctrinePersistence\TransactionTracker;
use SixtyEightPublishers\DoctrinePersistence\TransactionInterface;
use SixtyEightPublishers\DoctrinePersistence\FinallyCallbackQueueInvoker;

Expand All @@ -33,8 +34,9 @@ class TransactionFactoryTestCase extends TestCase
{
$em = Mockery::mock(EntityManagerInterface::class);
$finallyCallbackQueueInvoker = Mockery::mock(FinallyCallbackQueueInvoker::class);
$transactionTracker = new TransactionTracker();

$transactionFactory = new TransactionFactory($em, $finallyCallbackQueueInvoker);
$transactionFactory = new TransactionFactory($em, $finallyCallbackQueueInvoker, $transactionTracker);

Assert::type(TransactionInterface::class, $transactionFactory->create(static function () {
}, []));
Expand Down

0 comments on commit bf716e9

Please sign in to comment.