Skip to content

Commit 0fcf941

Browse files
authored
Merge pull request #43 from kbond/auto-register
[RFC] Add auto-register compiler pass
2 parents c5752d4 + cad85d2 commit 0fcf941

14 files changed

+241
-53
lines changed

doc/command_bus_bundle.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ services:
6464
- { name: command_handler, handles: Fully\Qualified\Class\Name\Of\RegisterUser }
6565
```
6666
67+
### Auto-Register command handlers
68+
69+
You can omit the `handles` tag attribute if your handler meets the following conditions:
70+
71+
1. Uses the "class_based" command name resolving strategy
72+
2. Handles a single command using the `__invoke` method
73+
3. Has a single, non optional class type hinted `__invoke` method parameter
74+
6775
> #### Command handlers are lazy-loaded
6876
>
6977
> Since only one of the command handlers is going to handle any particular command, command handlers are lazy-loaded.

doc/event_bus_bundle.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ services:
6565
- { name: event_subscriber, subscribes_to: Fully\Qualified\Class\Name\Of\UserRegistered }
6666
```
6767
68+
### Auto-Register event subscribers
69+
70+
You can omit the `subscribes_to` tag attribute if your subscriber meets the following conditions:
71+
72+
1. Uses the "class_based" event name resolving strategy
73+
2. Subscribers to single event using the `__invoke` method
74+
3. Has a single, non optional class type hinted `__invoke` method parameter
75+
6876
> #### Event subscribers are lazy-loaded
6977
>
7078
> Since only some of the event subscribers are going to handle any particular event, event subscribers are lazy-loaded.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace SimpleBus\SymfonyBridge\DependencyInjection\Compiler;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
8+
/**
9+
* @author Kevin Bond <kevinbond@gmail.com>
10+
*/
11+
final class AutoRegister implements CompilerPassInterface
12+
{
13+
private $tagName;
14+
private $tagAttribute;
15+
16+
public function __construct($tagName, $tagAttribute)
17+
{
18+
$this->tagName = $tagName;
19+
$this->tagAttribute = $tagAttribute;
20+
}
21+
22+
public function process(ContainerBuilder $container)
23+
{
24+
foreach ($container->findTaggedServiceIds($this->tagName) as $serviceId => $tags) {
25+
foreach ($tags as $tagAttributes) {
26+
27+
// if tag attributes are set, skip
28+
if (isset($tagAttributes[$this->tagAttribute])) {
29+
continue;
30+
}
31+
32+
$definition = $container->getDefinition($serviceId);
33+
34+
// check if service id is class name
35+
$reflectionClass = new \ReflectionClass($definition->getClass() ?: $serviceId);
36+
37+
// if no __invoke method, skip
38+
if (!$reflectionClass->hasMethod('__invoke')) {
39+
continue;
40+
}
41+
42+
$invokeParameters = $reflectionClass->getMethod('__invoke')->getParameters();
43+
44+
// if no param or optional param, skip
45+
if (count($invokeParameters) !== 1 || $invokeParameters[0]->isOptional()) {
46+
return;
47+
}
48+
49+
// get the class name
50+
$handles = $invokeParameters[0]->getClass()->getName();
51+
52+
// auto handle
53+
$definition->clearTag($this->tagName);
54+
$definition->addTag($this->tagName, [$this->tagAttribute => $handles]);
55+
}
56+
}
57+
}
58+
}

src/SimpleBusCommandBusBundle.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace SimpleBus\SymfonyBridge;
44

55
use SimpleBus\SymfonyBridge\DependencyInjection\CommandBusExtension;
6+
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AutoRegister;
67
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\ConfigureMiddlewares;
78
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterHandlers;
9+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
810
use Symfony\Component\DependencyInjection\ContainerBuilder;
911
use Symfony\Component\HttpKernel\Bundle\Bundle;
1012

@@ -19,6 +21,12 @@ public function __construct($alias = 'command_bus')
1921

2022
public function build(ContainerBuilder $container)
2123
{
24+
$container->addCompilerPass(
25+
new AutoRegister('command_handler', 'handles'),
26+
PassConfig::TYPE_BEFORE_OPTIMIZATION,
27+
10
28+
);
29+
2230
$container->addCompilerPass(
2331
new ConfigureMiddlewares(
2432
'command_bus',

src/SimpleBusEventBusBundle.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
namespace SimpleBus\SymfonyBridge;
44

55
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AddMiddlewareTags;
6+
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AutoRegister;
67
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\CompilerPassUtil;
78
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\ConfigureMiddlewares;
89
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterMessageRecorders;
910
use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterSubscribers;
1011
use SimpleBus\SymfonyBridge\DependencyInjection\EventBusExtension;
12+
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
1113
use Symfony\Component\DependencyInjection\ContainerBuilder;
1214
use Symfony\Component\HttpKernel\Bundle\Bundle;
1315

@@ -26,6 +28,12 @@ public function build(ContainerBuilder $container)
2628
{
2729
$this->checkRequirements(array('SimpleBusCommandBusBundle'), $container);
2830

31+
$container->addCompilerPass(
32+
new AutoRegister('event_subscriber', 'subscribes_to'),
33+
PassConfig::TYPE_BEFORE_OPTIMIZATION,
34+
10
35+
);
36+
2937
$container->addCompilerPass(
3038
new ConfigureMiddlewares(
3139
'event_bus',

tests/Functional/SmokeTest.php

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@
44

55
use Doctrine\ORM\EntityManager;
66
use Doctrine\ORM\Tools\SchemaTool;
7+
use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoCommand;
8+
use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoEvent;
79
use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand;
810
use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestKernel;
11+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
912
use Symfony\Component\DependencyInjection\ContainerInterface;
1013

11-
class SmokeTest extends \PHPUnit_Framework_TestCase
14+
class SmokeTest extends KernelTestCase
1215
{
16+
protected static function getKernelClass()
17+
{
18+
return TestKernel::class;
19+
}
20+
1321
/**
1422
* @test
1523
*/
1624
public function it_handles_a_command_then_dispatches_events_for_all_modified_entities()
1725
{
18-
$kernel = new TestKernel('test', true);
19-
$kernel->boot();
20-
$container = $kernel->getContainer();
26+
self::bootKernel(['environment' => 'config1']);
27+
$container = self::$kernel->getContainer();
2128

2229
$this->createSchema($container);
2330

@@ -42,6 +49,42 @@ public function it_handles_a_command_then_dispatches_events_for_all_modified_ent
4249
$this->assertContains('event_bus.DEBUG: Finished notifying a subscriber', $loggedMessages);
4350
}
4451

52+
/**
53+
* @test
54+
*/
55+
public function it_can_auto_register_event_subscribers()
56+
{
57+
self::bootKernel(['environment' => 'config2']);
58+
$container = self::$kernel->getContainer();
59+
60+
$subscriber = $container->get('auto_event_subscriber');
61+
$event = new AutoEvent();
62+
63+
$this->assertNull($subscriber->handled);
64+
65+
$container->get('event_bus')->handle($event);
66+
67+
$this->assertSame($event, $subscriber->handled);
68+
}
69+
70+
/**
71+
* @test
72+
*/
73+
public function it_can_auto_register_command_handlers()
74+
{
75+
self::bootKernel(['environment' => 'config2']);
76+
$container = self::$kernel->getContainer();
77+
78+
$handler = $container->get('auto_command_handler');
79+
$command = new AutoCommand();
80+
81+
$this->assertNull($handler->handled);
82+
83+
$container->get('command_bus')->handle($command);
84+
85+
$this->assertSame($command, $handler->handled);
86+
}
87+
4588
private function createSchema(ContainerInterface $container)
4689
{
4790
$entityManager = $container->get('doctrine.orm.entity_manager');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto;
4+
5+
final class AutoCommand
6+
{
7+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto;
4+
5+
final class AutoCommandHandler
6+
{
7+
public $handled;
8+
9+
public function __invoke(AutoCommand $command)
10+
{
11+
$this->handled = $command;
12+
}
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto;
4+
5+
final class AutoEvent
6+
{
7+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto;
4+
5+
final class AutoEventSubscriber
6+
{
7+
public $handled;
8+
9+
public function __invoke(AutoEvent $event)
10+
{
11+
$this->handled = $event;
12+
}
13+
}

tests/Functional/SmokeTest/TestKernel.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ public function __construct($environment, $debug)
1818
{
1919
parent::__construct($environment, $debug);
2020

21-
$this->tempDir = sys_get_temp_dir() . '/' . uniqid();
22-
mkdir($this->tempDir, 0777, true);
21+
$this->tempDir = sys_get_temp_dir() . '/simplebus-symfony-bridge';
2322
}
2423

2524
public function registerBundles()
@@ -35,7 +34,7 @@ public function registerBundles()
3534

3635
public function registerContainerConfiguration(LoaderInterface $loader)
3736
{
38-
$loader->load(__DIR__ . '/config.yml');
37+
$loader->load(sprintf('%s/%s.yml', __DIR__, $this->environment));
3938
}
4039

4140
public function getCacheDir()

tests/Functional/SmokeTest/config.yml

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,7 @@
1-
parameters:
2-
log_file: %kernel.logs_dir%/%kernel.environment%.log
3-
41
services:
52
annotation_reader:
63
class: Doctrine\Common\Annotations\AnnotationReader
74

8-
test_command_handler:
9-
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommandHandler
10-
arguments:
11-
- '@doctrine.orm.default_entity_manager'
12-
tags:
13-
- { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand }
14-
15-
some_other_test_command_handler:
16-
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommandHandler
17-
arguments:
18-
- '@event_recorder'
19-
tags:
20-
- { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommand }
21-
22-
test_event_subscriber:
23-
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestEntityCreatedEventSubscriber
24-
tags:
25-
- { name: event_subscriber, subscribes_to: test_entity_created }
26-
arguments:
27-
- '@command_bus'
28-
29-
some_other_event_subscriber:
30-
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherEventSubscriber
31-
tags:
32-
- { name: event_subscriber, subscribes_to: some_other_event }
33-
arguments:
34-
- '@command_bus'
35-
365
doctrine:
376
dbal:
387
driver: pdo_sqlite
@@ -49,18 +18,3 @@ doctrine:
4918
prefix: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Entity
5019
alias: Test
5120
is_bundle: false
52-
53-
command_bus:
54-
command_name_resolver_strategy: class_based
55-
logging: ~
56-
57-
event_bus:
58-
event_name_resolver_strategy: named_message
59-
logging: ~
60-
61-
monolog:
62-
handlers:
63-
main:
64-
type: stream
65-
path: %log_file%
66-
level: debug
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
imports:
2+
- { resource: config.yml }
3+
4+
parameters:
5+
log_file: %kernel.logs_dir%/%kernel.environment%.log
6+
7+
services:
8+
test_command_handler:
9+
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommandHandler
10+
arguments:
11+
- '@doctrine.orm.default_entity_manager'
12+
tags:
13+
- { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand }
14+
15+
some_other_test_command_handler:
16+
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommandHandler
17+
arguments:
18+
- '@event_recorder'
19+
tags:
20+
- { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommand }
21+
22+
test_event_subscriber:
23+
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestEntityCreatedEventSubscriber
24+
tags:
25+
- { name: event_subscriber, subscribes_to: test_entity_created }
26+
arguments:
27+
- '@command_bus'
28+
29+
some_other_event_subscriber:
30+
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherEventSubscriber
31+
tags:
32+
- { name: event_subscriber, subscribes_to: some_other_event }
33+
arguments:
34+
- '@command_bus'
35+
36+
command_bus:
37+
command_name_resolver_strategy: class_based
38+
logging: ~
39+
40+
event_bus:
41+
event_name_resolver_strategy: named_message
42+
logging: ~
43+
44+
monolog:
45+
handlers:
46+
main:
47+
type: stream
48+
path: %log_file%
49+
level: debug
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
imports:
2+
- { resource: config.yml }
3+
4+
services:
5+
auto_command_handler:
6+
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoCommandHandler
7+
tags:
8+
- { name: command_handler }
9+
10+
auto_event_subscriber:
11+
class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoEventSubscriber
12+
tags:
13+
- { name: event_subscriber }

0 commit comments

Comments
 (0)