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 <[email protected]>
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+
}

0 commit comments

Comments
 (0)