Skip to content

Commit

Permalink
Merge pull request #29 from tyx/allow_tagging_handlers_per_bus
Browse files Browse the repository at this point in the history
Allow tagging handlers per bus
  • Loading branch information
rosstuck committed Nov 16, 2016
2 parents 1d9788a + c4873f5 commit 8a00e7f
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 114 deletions.
101 changes: 92 additions & 9 deletions DependencyInjection/Compiler/CommandHandlerPass.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<?php
namespace League\Tactician\Bundle\DependencyInjection\Compiler;

use League\Tactician\Bundle\Handler\ContainerBasedHandlerLocator;
use League\Tactician\Handler\CommandHandlerMiddleware;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

/**
* This compiler pass maps Handler DI tags to specific commands
Expand All @@ -19,25 +23,104 @@ class CommandHandlerPass implements CompilerPassInterface
*/
public function process(ContainerBuilder $container)
{
if (!$container->has('tactician.handler.locator.symfony')) {
throw new \Exception('Missing tactician.handler.locator.symfony definition');
}

$handlerLocator = $container->findDefinition('tactician.handler.locator.symfony');

$mapping = [];
$tacticianConfig = $container->getExtensionConfig('tactician');
$defaultBusId = $tacticianConfig['default_bus'];
$busIds = array_keys($tacticianConfig['commandbus']);
$busIdToHandlerMapping = [];

foreach ($container->findTaggedServiceIds('tactician.handler') as $id => $tags) {

foreach ($tags as $attributes) {
if (!isset($attributes['command'])) {
throw new \Exception('The tactician.handler tag must always have a command attribute');
}

$mapping[$attributes['command']] = $id;
if (array_key_exists('bus', $attributes)) {
$this->abortIfInvalidBusId($attributes['bus'], $busIds);
}

$busIdsDefined = array_key_exists('bus', $attributes) ? [$attributes['bus']] : $busIds;
foreach ($busIdsDefined as $busId) {
$busIdToHandlerMapping[$busId][$attributes['command']] = $id;
}
}
}

$handlerLocator->addArgument($mapping);
foreach ($busIdToHandlerMapping as $busId => $handlerMapping) {
$locatorServiceId = 'tactician.commandbus.'.$busId.'.handler.locator';
$container->setDefinition(
$locatorServiceId,
$this->buildLocatorDefinition($handlerMapping)
);

$container->setDefinition(
'tactician.commandbus.'.$busId.'.middleware.command_handler',
$this->buildCommandHandlerDefinition($busId, $locatorServiceId, $tacticianConfig)
);
}

$container->setAlias(
'tactician.handler.locator.symfony',
'tactician.commandbus.'.$defaultBusId.'.handler.locator'
);

$container->setAlias(
'tactician.middleware.command_handler',
'tactician.commandbus.'.$defaultBusId.'.middleware.command_handler'
);
}

/**
* @param string $id
* @param array $busIds
* @throws Exception
*/
protected function abortIfInvalidBusId($id, array $busIds)
{
if (!in_array($id, $busIds)) {
throw new \Exception('Invalid bus id "'.$id.'". Valid buses are: '.implode(', ', $busIds));
}
}

/**
* @param array $handlerMapping
* @return Definition
*/
protected function buildLocatorDefinition(array $handlerMapping)
{
return new Definition(
ContainerBasedHandlerLocator::class,
[
new Reference('service_container'),
$handlerMapping,
]
);
}

/**
* @param string $busId
* @param string $locatorServiceId
* @param array $config
* @return Definition
*/
protected function buildCommandHandlerDefinition($busId, $locatorServiceId, array $config)
{
return new Definition(
CommandHandlerMiddleware::class,
[
new Reference('tactician.handler.command_name_extractor.class_name'),
new Reference($locatorServiceId),
new Reference($this->methodInflectorOfBus($busId, $config))
]
);
}

private function methodInflectorOfBus($busId, array $config)
{
if (array_key_exists('method_inflector', $config['commandbus'][$busId])) {
return $config['commandbus'][$busId]['method_inflector'];
}

return $config['method_inflector'];
}
}
2 changes: 1 addition & 1 deletion DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public function getConfigTreeBuilder()
)
->end()
->end()
->scalarNode('method_inflector')->end()
->end()

->end()
->end()
->scalarNode('default_bus')
Expand Down
20 changes: 0 additions & 20 deletions DependencyInjection/TacticianExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
$loader->load('services.yml');

$this->configureCommandBuses($mergedConfig, $container);
$this->injectMethodNameInflector($mergedConfig, $container);
$this->configureSecurity($mergedConfig, $container);
}

Expand Down Expand Up @@ -55,25 +54,6 @@ function ($middlewareServiceId) {
}
}

/**
* Define the default Method Name Inflector.
* This will fail silently if the command_handler service does not exist
*
* @param array $mergedConfig
* @param ContainerBuilder $container
*/
private function injectMethodNameInflector(array $mergedConfig, ContainerBuilder $container)
{
if (! $container->has('tactician.middleware.command_handler')) {
return;
}

$inflectorReference = new Reference($mergedConfig['method_inflector']);

$handlerLocator = $container->findDefinition('tactician.middleware.command_handler');
$handlerLocator->replaceArgument(2, $inflectorReference);
}

/**
* Configure the security voter if the security middleware is loaded.
*
Expand Down
4 changes: 4 additions & 0 deletions Handler/ContainerBasedHandlerLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class ContainerBasedHandlerLocator implements HandlerLocator
*/
public function __construct(ContainerInterface $container, $commandToServiceIdMapping)
{
if (empty($commandToServiceIdMapping)) {
throw new \InvalidArgumentException('commandToServiceIdMapping cannot be empty');
}

$this->container = $container;
$this->commandToServiceId = $commandToServiceIdMapping;
}
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ tactician:
# ...
```

By default, all commands are available in each bus. If you want to make a command available only in a specific bus, you need to specify its id :

```yaml
foo.user.register_user_handler:
class: Foo\User\RegisterUserHandler
arguments:
- '@foo.user.user_repository'
tags:
- { name: tactician.handler, command: Foo\User\RegisterUserCommand, bus: queued }
```

### Extra Bundled Middleware

This bundles ships with a few pre-configured middlewares, they can be enabled using the method above by just listing their ids.
Expand Down Expand Up @@ -185,6 +196,20 @@ Tactician offers a list of custom Inflectors, these are all supported.
* `tactician.handler.method_name_inflector.handle_class_name_without_suffix`
* `tactician.handler.method_name_inflector.invoke`

When using multiple bus, you can also specify `method_inflector` of particular bus :

```yaml
tactician:
commandbus:
command:
middleware:
- tactician.middleware.command_handler
query:
middleware:
- tactician.middleware.command_handler
method_inflector: tactician.handler.method_name_inflector.handle_class_name_without_suffix
```

## Using the Command Bus
Create a service and inject the command bus:

Expand Down
13 changes: 0 additions & 13 deletions Resources/config/services/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,6 @@ parameters:
tactician.commandbus.class: League\Tactician\CommandBus

services:
tactician.handler.locator.symfony:
class: League\Tactician\Bundle\Handler\ContainerBasedHandlerLocator
arguments:
- "@service_container"

# The standard middleware
tactician.middleware.command_handler:
class: League\Tactician\Handler\CommandHandlerMiddleware
arguments:
- "@tactician.handler.command_name_extractor.class_name"
- "@tactician.handler.locator.symfony"
- "@tactician.handler.method_name_inflector.handle"

tactician.middleware.locking:
class: League\Tactician\Plugins\LockingMiddleware

Expand Down
Loading

0 comments on commit 8a00e7f

Please sign in to comment.