Last active
April 22, 2018 12:45
-
-
Save dadamssg/e1bea7329e8bf93c8b04 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\Project\Bundle\AppBundle\Data; | |
use Acme\Project\Model\App\Data\TransactionManager; | |
use Doctrine\ORM\EntityManagerInterface; | |
class DoctrineTransactionManager extends TransactionManager | |
{ | |
/** | |
* @var EntityManagerInterface | |
*/ | |
private $em; | |
/** | |
* @param EntityManagerInterface $em | |
*/ | |
public function __construct(EntityManagerInterface $em) | |
{ | |
$this->em = $em; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function begin() | |
{ | |
$this->getConnection()->beginTransaction(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function commit() | |
{ | |
$this->getConnection()->commit(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function rollback() | |
{ | |
$this->em->close(); | |
$this->getConnection()->rollBack(); | |
} | |
/** | |
* @return \Doctrine\DBAL\Connection | |
*/ | |
private function getConnection() | |
{ | |
return $this->em->getConnection(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
app.register_user_command_handler: | |
class: Acme\Project\Model\User\Handler\RegisterUserHandler | |
arguments: | |
- @app.user_repository | |
- @app.password_encoder | |
tags: | |
- { name: command_handler, handles: Acme\Project\Model\User\Command\RegisterUserCommand } | |
- { name: wrap_in_transaction } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\Project\Bundle\AppBundle\Middleware; | |
use Acme\Project\Model\App\Data\TransactionManager; | |
class TransactionalHandler | |
{ | |
/** | |
* @var TransactionManager | |
*/ | |
private $tx; | |
/** | |
* @var callable | |
*/ | |
private $handler; | |
/** | |
* @var string | |
*/ | |
private $method; | |
/** | |
* @param TransactionManager $tx | |
* @param callable $handler | |
* @param string $method | |
*/ | |
public function __construct(TransactionManager $tx, $handler, $method = null) | |
{ | |
$this->tx = $tx; | |
$this->handler = $handler; | |
$this->method = $method; | |
} | |
public function handle($command) | |
{ | |
$method = $this->method ?: '__invoke'; | |
$method = method_exists($this->handler, $method) ? $method : 'handle'; | |
if (!method_exists($this->handler, $method)) { | |
throw new \Exception("Command handler has no method to handle command."); | |
} | |
$this->tx->transactional(function () use ($command, $method) { | |
$this->handler->$method($command); | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\Project\Bundle\AppBundle\DependencyInjection\Compiler; | |
use Acme\Project\Bundle\AppBundle\Data\DoctrineTransactionManager; | |
use Acme\Project\Bundle\AppBundle\Middleware\TransactionalHandler; | |
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | |
use Symfony\Component\DependencyInjection\ContainerBuilder; | |
use Symfony\Component\DependencyInjection\Definition; | |
use Symfony\Component\DependencyInjection\Reference; | |
class WrapHandlerInTransactionCompilerPass implements CompilerPassInterface | |
{ | |
const TRANSACTION_MANAGER_ID = 'app.transaction_manager'; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function process(ContainerBuilder $container) | |
{ | |
$this->registerTransactionManager($container); | |
$taggedServices = $container->findTaggedServiceIds('wrap_in_transaction'); | |
foreach ($taggedServices as $id => $tags) { | |
$def = $container->findDefinition($id); | |
$commandHandlerTags = $def->getTag('command_handler'); | |
if (!$commandHandlerTags) { | |
throw new \Exception(sprintf('Service "%s" must be tagged as a command handler to be wrapped in a transaction.', $id)); | |
} | |
// remove command_handler tags so handler isn't | |
// directly interacted with by message bus | |
$def->clearTag('command_handler'); | |
$originalDefId = $this->renameOriginalDefinition($container, $def, $id); | |
// A single handler could be responsible for handling different | |
// commands via different methods so we create new handlers | |
// for each handler::method | |
foreach ($commandHandlerTags as $tag) { | |
$handlerMethod = isset($tag['method']) ? $tag['method'] : null; | |
// set method so TransactionalHandler::handle() will be called | |
$tag['method'] = 'handle'; | |
$container->register($id, TransactionalHandler::CLASS) | |
->addArgument(new Reference(self::TRANSACTION_MANAGER_ID)) | |
->addArgument(new Reference($originalDefId)) | |
->addArgument($handlerMethod) | |
->addTag('command_handler', $tag); | |
} | |
} | |
} | |
/** | |
* @param ContainerBuilder $container | |
*/ | |
private function registerTransactionManager(ContainerBuilder $container) | |
{ | |
$emId = sprintf("doctrine.orm.%s_entity_manager", $container->getParameter('command_bus_em_name')); | |
$container->register(self::TRANSACTION_MANAGER_ID, DoctrineTransactionManager::CLASS) | |
->addArgument(new Reference($emId)); | |
} | |
/** | |
* @param ContainerBuilder $container | |
* @param Definition$def | |
* @param string $id | |
* @return string | |
*/ | |
public function renameOriginalDefinition(ContainerBuilder $container, Definition $def, $id) | |
{ | |
$container->removeDefinition($id); | |
$originalDefId = sprintf("tx_wrapped.%s", $id); | |
$container->setDefinition($originalDefId, $def); | |
return $originalDefId; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment