Skip to content

Commit

Permalink
Add support for symphony/dependency-injection
Browse files Browse the repository at this point in the history
  • Loading branch information
olvlvl committed Sep 13, 2017
1 parent 4a4ac52 commit 2c07b3b
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 10 deletions.
80 changes: 71 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ The message handler provider is a callable with a signature similar to the
that only requires an array of key/value pairs, where _key_ is a message class and _value_
a message handler callable.

### Providing handlers

The following example demonstrates how to define a message handler provider with a selection
of messages and their handlers:

Expand All @@ -73,6 +75,8 @@ $message_handler_provider = new SimpleMessageHandlerProvider([
]);
```

### Providing handlers with icanboogie/service

Of course, if you're using the [icanboogie/service][] package, you can use service references
instead of callables (well, technically, they are also callables):

Expand All @@ -95,6 +99,62 @@ $message_handler_provider = new SimpleMessageHandlerProvider([



### Providing handlers with a PSR container

Use an instance of [PSR\ContainerMessageHandlerProvider][] to provide handlers from a
[PSR container][]:

```php
<?php

use App\Application\Message;
use ICanBoogie\MessageBus\PSR\ContainerMessageHandlerProvider;

/* @var $container \Psr\Container\ContainerInterface */

$message_handler_provider = new ContainerMessageHandlerProvider([

Message\CreateArticle::class => 'handler.article.create',
Message\DeleteArticle::class => 'handler.article.delete',

], $container);
```

If you're using [symfony/dependency-injection][] you can add an instance of [AddCommandBusPass][]
to your compilation pass to automatically generate the provider:

```yaml
services:
handler.article.create:
class: App\Domain\Article\Handler\CreateArticleHandler
tags:
- name: message_bus.handler
message: App\Application\Message\CreateArticle
```
```php
<?php

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
use ICanBoogie\MessageBus\Symfony\AddCommandBusPass;

/* @var string $config */
/* @var ICanBoogie\MessageBus\PSR\ContainerMessageHandlerProvider $provider */

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load($config);
$container->addCompilerPass(new AddCommandBusPass);
$container->compile();

$provider = $container->get(AddCommandBusPass::DEFAULT_PROVIDER_SERVICE);
```





----------

Expand All @@ -114,9 +174,7 @@ The package requires PHP 5.6 or later.

The recommended way to install this package is through [Composer](http://getcomposer.org/):

```
$ composer require icanboogie/message-bus
```
$ composer require icanboogie/message-bus



Expand Down Expand Up @@ -169,9 +227,13 @@ The package is continuously tested by [Travis CI](http://about.travis-ci.org/).



[documentation]: https://icanboogie.org/api/message-bus/master/
[MessageHandlerProvider]: https://icanboogie.org/api/message-bus/master/class-ICanBoogie.MessageBus.MessageHandlerProvider.html
[ShouldBePushed]: https://icanboogie.org/api/message-bus/master/class-ICanBoogie.MessageBus.ShouldBePushed.html
[available on GitHub]: https://github.com/ICanBoogie/MessageBus
[icanboogie/service]: https://github.com/ICanBoogie/Service
[ICanBoogie]: https://icanboogie.org
[documentation]: https://icanboogie.org/api/message-bus/master/
[MessageHandlerProvider]: https://icanboogie.org/api/message-bus/master/class-ICanBoogie.MessageBus.MessageHandlerProvider.html
[ShouldBePushed]: https://icanboogie.org/api/message-bus/master/class-ICanBoogie.MessageBus.ShouldBePushed.html
[AddCommandBusPass]: https://icanboogie.org/api/message-bus/master/class-ICanBoogie.MessageBus.Symfony.AddCommandBusPass.html
[available on GitHub]: https://github.com/ICanBoogie/MessageBus
[icanboogie/service]: https://github.com/ICanBoogie/Service
[PSR container]: https://github.com/php-fig/container
[ICanBoogie]: https://icanboogie.org
[PSR\ContainerMessageHandlerProvider]: https://icanboogie.org/api/message-bus/master/class-ICanBoogie.MessageBus.PSR.ContainerMessageHandlerProvider.html
[symfony/dependency-injection]: https://symfony.com/doc/current/components/dependency_injection.html
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
"icanboogie/accessor": "^1.0"
},
"require-dev": {
"psr/container": "^1.0"
"psr/container": "^1.0",
"symfony/dependency-injection": "^3.3",
"symfony/config": "^3.3",
"symfony/yaml": "^3.3"
},
"autoload": {
"psr-4": {
Expand Down
99 changes: 99 additions & 0 deletions lib/Symfony/AddCommandBusPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

/*
* This file is part of the ICanBoogie package.
*
* (c) Olivier Laviale <olivier.laviale@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ICanBoogie\MessageBus\Symfony;

use ICanBoogie\MessageBus\PSR\ContainerMessageHandlerProvider;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\TypedReference;

/**
* Registers command bus handlers.
*/
class AddCommandBusPass implements CompilerPassInterface
{
const DEFAULT_PROVIDER_SERVICE = 'message_bus.message_handler_provider';
const DEFAULT_HANDLER_TAG = 'message_bus.handler';
const DEFAULT_MESSAGE_PROPERTY = 'message';

/**
* @var string
*/
private $providerId;

/**
* @var string
*/
private $handlerTag;

/**
* @var string
*/
private $messageProperty;

/**
* @param string $providerId
* @param string $handlerTag
* @param string $messageProperty
*/
public function __construct(
$providerId = self::DEFAULT_PROVIDER_SERVICE,
$handlerTag = self::DEFAULT_HANDLER_TAG,
$messageProperty = self::DEFAULT_MESSAGE_PROPERTY
) {
$this->providerId = $providerId;
$this->handlerTag = $handlerTag;
$this->messageProperty = $messageProperty;
}

/**
* @inheritdoc
*/
public function process(ContainerBuilder $container)
{
$handlers = $container->findTaggedServiceIds($this->handlerTag, true);
$messageProperty = $this->messageProperty;
$mapping = [];
$handlerRefs = [];

foreach ($handlers as $id => $tags) {
if (empty($tags[0][$messageProperty]))
{
throw new InvalidArgumentException(
"The `$messageProperty` property is required for service `$id`."
);
}

$command = $tags[0][$messageProperty];

if (isset($mapping[$command]))
{
throw new LogicException(
"The command `$command` already has an handler: `{$mapping[$command]}`."
);
}

$mapping[$command] = $id;
$handlerRefs[$id] = new TypedReference($id, $container->getDefinition($id)->getClass());
}

$container
->register($this->providerId, ContainerMessageHandlerProvider::class)
->setArguments([
$mapping,
ServiceLocatorTagPass::register($container, $handlerRefs)
]);
}
}
11 changes: 11 additions & 0 deletions tests/HandlerA.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace ICanBoogie\MessageBus;

class HandlerA
{
public function __invoke()
{

}
}
11 changes: 11 additions & 0 deletions tests/HandlerB.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace ICanBoogie\MessageBus;

class HandlerB
{
public function __invoke()
{

}
}
69 changes: 69 additions & 0 deletions tests/Symfony/AddCommandBusPassTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/*
* This file is part of the ICanBoogie package.
*
* (c) Olivier Laviale <olivier.laviale@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace ICanBoogie\MessageBus\Symfony;

use ICanBoogie\MessageBus\HandlerA;
use ICanBoogie\MessageBus\HandlerB;
use ICanBoogie\MessageBus\MessageA;
use ICanBoogie\MessageBus\MessageB;
use ICanBoogie\MessageBus\PSR\ContainerMessageHandlerProvider;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder as SymfonyContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

class AddCommandBusPassTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The `message` property is required for service `handler.message_a`
*/
public function test_should_error_on_missing_message()
{
$this->makeContainer(__DIR__ . '/resources/missing-message.yml');
}

/**
* @expectedException \LogicException
* @expectedExceptionMessage The command `ICanBoogie\MessageBus\MessageA` already has an handler: `handler.message_a`.
*/
public function test_should_error_message_duplicated()
{
$this->makeContainer(__DIR__ . '/resources/message-duplicate.yml');
}

public function test_should_return_handler()
{
/* @var ContainerMessageHandlerProvider $provider */
$container = $this->makeContainer(__DIR__ . '/resources/ok.yml');
$provider = $container->get(AddCommandBusPass::DEFAULT_PROVIDER_SERVICE);

$this->assertInstanceOf(ContainerMessageHandlerProvider::class, $provider);
$this->assertInstanceOf(HandlerA::class, $provider(new MessageA()));
$this->assertInstanceOf(HandlerB::class, $provider(new MessageB()));
}

/**
* @param string $config
*
* @return SymfonyContainerBuilder
*/
private function makeContainer($config)
{
$container = new SymfonyContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load($config);
$container->addCompilerPass(new AddCommandBusPass);
$container->compile();

return $container;
}
}
13 changes: 13 additions & 0 deletions tests/Symfony/resources/message-duplicate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:

handler.message_a:
class: ICanBoogie\MessageBus\HandlerA
tags:
- name: message_bus.handler
message: ICanBoogie\MessageBus\MessageA

handler.message_b:
class: ICanBoogie\MessageBus\HandlerB
tags:
- name: message_bus.handler
message: ICanBoogie\MessageBus\MessageA
6 changes: 6 additions & 0 deletions tests/Symfony/resources/missing-message.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:

handler.message_a:
class: ICanBoogie\MessageBus\HandlerA
tags:
- name: message_bus.handler
13 changes: 13 additions & 0 deletions tests/Symfony/resources/ok.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:

handler.message_a:
class: ICanBoogie\MessageBus\HandlerA
tags:
- name: message_bus.handler
message: ICanBoogie\MessageBus\MessageA

handler.message_b:
class: ICanBoogie\MessageBus\HandlerB
tags:
- name: message_bus.handler
message: ICanBoogie\MessageBus\MessageB

0 comments on commit 2c07b3b

Please sign in to comment.