diff --git a/doc/command_bus_bundle.md b/doc/command_bus_bundle.md index f1156a1..120c9dd 100644 --- a/doc/command_bus_bundle.md +++ b/doc/command_bus_bundle.md @@ -64,6 +64,14 @@ services: - { name: command_handler, handles: Fully\Qualified\Class\Name\Of\RegisterUser } ``` +### Auto-Register command handlers + +You can omit the `handles` tag attribute if your handler meets the following conditions: + +1. Uses the "class_based" command name resolving strategy +2. Handles a single command using the `__invoke` method +3. Has a single, non optional class type hinted `__invoke` method parameter + > #### Command handlers are lazy-loaded > > Since only one of the command handlers is going to handle any particular command, command handlers are lazy-loaded. diff --git a/doc/event_bus_bundle.md b/doc/event_bus_bundle.md index 3389b25..ddecf6c 100644 --- a/doc/event_bus_bundle.md +++ b/doc/event_bus_bundle.md @@ -65,6 +65,14 @@ services: - { name: event_subscriber, subscribes_to: Fully\Qualified\Class\Name\Of\UserRegistered } ``` +### Auto-Register event subscribers + +You can omit the `subscribes_to` tag attribute if your subscriber meets the following conditions: + +1. Uses the "class_based" event name resolving strategy +2. Subscribers to single event using the `__invoke` method +3. Has a single, non optional class type hinted `__invoke` method parameter + > #### Event subscribers are lazy-loaded > > Since only some of the event subscribers are going to handle any particular event, event subscribers are lazy-loaded. diff --git a/src/DependencyInjection/Compiler/AutoRegister.php b/src/DependencyInjection/Compiler/AutoRegister.php new file mode 100644 index 0000000..ab7af1f --- /dev/null +++ b/src/DependencyInjection/Compiler/AutoRegister.php @@ -0,0 +1,58 @@ + + */ +final class AutoRegister implements CompilerPassInterface +{ + private $tagName; + private $tagAttribute; + + public function __construct($tagName, $tagAttribute) + { + $this->tagName = $tagName; + $this->tagAttribute = $tagAttribute; + } + + public function process(ContainerBuilder $container) + { + foreach ($container->findTaggedServiceIds($this->tagName) as $serviceId => $tags) { + foreach ($tags as $tagAttributes) { + + // if tag attributes are set, skip + if (isset($tagAttributes[$this->tagAttribute])) { + continue; + } + + $definition = $container->getDefinition($serviceId); + + // check if service id is class name + $reflectionClass = new \ReflectionClass($definition->getClass() ?: $serviceId); + + // if no __invoke method, skip + if (!$reflectionClass->hasMethod('__invoke')) { + continue; + } + + $invokeParameters = $reflectionClass->getMethod('__invoke')->getParameters(); + + // if no param or optional param, skip + if (count($invokeParameters) !== 1 || $invokeParameters[0]->isOptional()) { + return; + } + + // get the class name + $handles = $invokeParameters[0]->getClass()->getName(); + + // auto handle + $definition->clearTag($this->tagName); + $definition->addTag($this->tagName, [$this->tagAttribute => $handles]); + } + } + } +} diff --git a/src/SimpleBusCommandBusBundle.php b/src/SimpleBusCommandBusBundle.php index ca060b9..3232389 100644 --- a/src/SimpleBusCommandBusBundle.php +++ b/src/SimpleBusCommandBusBundle.php @@ -3,8 +3,10 @@ namespace SimpleBus\SymfonyBridge; use SimpleBus\SymfonyBridge\DependencyInjection\CommandBusExtension; +use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AutoRegister; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\ConfigureMiddlewares; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterHandlers; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -19,6 +21,12 @@ public function __construct($alias = 'command_bus') public function build(ContainerBuilder $container) { + $container->addCompilerPass( + new AutoRegister('command_handler', 'handles'), + PassConfig::TYPE_BEFORE_OPTIMIZATION, + 10 + ); + $container->addCompilerPass( new ConfigureMiddlewares( 'command_bus', diff --git a/src/SimpleBusEventBusBundle.php b/src/SimpleBusEventBusBundle.php index 6fd6502..d8b7b84 100644 --- a/src/SimpleBusEventBusBundle.php +++ b/src/SimpleBusEventBusBundle.php @@ -3,11 +3,13 @@ namespace SimpleBus\SymfonyBridge; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AddMiddlewareTags; +use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\AutoRegister; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\CompilerPassUtil; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\ConfigureMiddlewares; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterMessageRecorders; use SimpleBus\SymfonyBridge\DependencyInjection\Compiler\RegisterSubscribers; use SimpleBus\SymfonyBridge\DependencyInjection\EventBusExtension; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -26,6 +28,12 @@ public function build(ContainerBuilder $container) { $this->checkRequirements(array('SimpleBusCommandBusBundle'), $container); + $container->addCompilerPass( + new AutoRegister('event_subscriber', 'subscribes_to'), + PassConfig::TYPE_BEFORE_OPTIMIZATION, + 10 + ); + $container->addCompilerPass( new ConfigureMiddlewares( 'event_bus', diff --git a/tests/Functional/SmokeTest.php b/tests/Functional/SmokeTest.php index 0d0cf95..16869b6 100644 --- a/tests/Functional/SmokeTest.php +++ b/tests/Functional/SmokeTest.php @@ -4,20 +4,27 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\SchemaTool; +use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoCommand; +use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoEvent; use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand; use SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestKernel; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\DependencyInjection\ContainerInterface; -class SmokeTest extends \PHPUnit_Framework_TestCase +class SmokeTest extends KernelTestCase { + protected static function getKernelClass() + { + return TestKernel::class; + } + /** * @test */ public function it_handles_a_command_then_dispatches_events_for_all_modified_entities() { - $kernel = new TestKernel('test', true); - $kernel->boot(); - $container = $kernel->getContainer(); + self::bootKernel(['environment' => 'config1']); + $container = self::$kernel->getContainer(); $this->createSchema($container); @@ -42,6 +49,42 @@ public function it_handles_a_command_then_dispatches_events_for_all_modified_ent $this->assertContains('event_bus.DEBUG: Finished notifying a subscriber', $loggedMessages); } + /** + * @test + */ + public function it_can_auto_register_event_subscribers() + { + self::bootKernel(['environment' => 'config2']); + $container = self::$kernel->getContainer(); + + $subscriber = $container->get('auto_event_subscriber'); + $event = new AutoEvent(); + + $this->assertNull($subscriber->handled); + + $container->get('event_bus')->handle($event); + + $this->assertSame($event, $subscriber->handled); + } + + /** + * @test + */ + public function it_can_auto_register_command_handlers() + { + self::bootKernel(['environment' => 'config2']); + $container = self::$kernel->getContainer(); + + $handler = $container->get('auto_command_handler'); + $command = new AutoCommand(); + + $this->assertNull($handler->handled); + + $container->get('command_bus')->handle($command); + + $this->assertSame($command, $handler->handled); + } + private function createSchema(ContainerInterface $container) { $entityManager = $container->get('doctrine.orm.entity_manager'); diff --git a/tests/Functional/SmokeTest/Auto/AutoCommand.php b/tests/Functional/SmokeTest/Auto/AutoCommand.php new file mode 100644 index 0000000..543e20a --- /dev/null +++ b/tests/Functional/SmokeTest/Auto/AutoCommand.php @@ -0,0 +1,7 @@ +handled = $command; + } +} diff --git a/tests/Functional/SmokeTest/Auto/AutoEvent.php b/tests/Functional/SmokeTest/Auto/AutoEvent.php new file mode 100644 index 0000000..e2aaa81 --- /dev/null +++ b/tests/Functional/SmokeTest/Auto/AutoEvent.php @@ -0,0 +1,7 @@ +handled = $event; + } +} diff --git a/tests/Functional/SmokeTest/TestKernel.php b/tests/Functional/SmokeTest/TestKernel.php index f48e2c9..e93afa5 100644 --- a/tests/Functional/SmokeTest/TestKernel.php +++ b/tests/Functional/SmokeTest/TestKernel.php @@ -18,8 +18,7 @@ public function __construct($environment, $debug) { parent::__construct($environment, $debug); - $this->tempDir = sys_get_temp_dir() . '/' . uniqid(); - mkdir($this->tempDir, 0777, true); + $this->tempDir = sys_get_temp_dir() . '/simplebus-symfony-bridge'; } public function registerBundles() @@ -35,7 +34,7 @@ public function registerBundles() public function registerContainerConfiguration(LoaderInterface $loader) { - $loader->load(__DIR__ . '/config.yml'); + $loader->load(sprintf('%s/%s.yml', __DIR__, $this->environment)); } public function getCacheDir() diff --git a/tests/Functional/SmokeTest/config.yml b/tests/Functional/SmokeTest/config.yml index fd1b3c9..18e2b77 100644 --- a/tests/Functional/SmokeTest/config.yml +++ b/tests/Functional/SmokeTest/config.yml @@ -1,38 +1,7 @@ -parameters: - log_file: %kernel.logs_dir%/%kernel.environment%.log - services: annotation_reader: class: Doctrine\Common\Annotations\AnnotationReader - test_command_handler: - class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommandHandler - arguments: - - '@doctrine.orm.default_entity_manager' - tags: - - { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand } - - some_other_test_command_handler: - class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommandHandler - arguments: - - '@event_recorder' - tags: - - { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommand } - - test_event_subscriber: - class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestEntityCreatedEventSubscriber - tags: - - { name: event_subscriber, subscribes_to: test_entity_created } - arguments: - - '@command_bus' - - some_other_event_subscriber: - class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherEventSubscriber - tags: - - { name: event_subscriber, subscribes_to: some_other_event } - arguments: - - '@command_bus' - doctrine: dbal: driver: pdo_sqlite @@ -49,18 +18,3 @@ doctrine: prefix: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Entity alias: Test is_bundle: false - -command_bus: - command_name_resolver_strategy: class_based - logging: ~ - -event_bus: - event_name_resolver_strategy: named_message - logging: ~ - -monolog: - handlers: - main: - type: stream - path: %log_file% - level: debug diff --git a/tests/Functional/SmokeTest/config1.yml b/tests/Functional/SmokeTest/config1.yml new file mode 100644 index 0000000..5be937d --- /dev/null +++ b/tests/Functional/SmokeTest/config1.yml @@ -0,0 +1,49 @@ +imports: + - { resource: config.yml } + +parameters: + log_file: %kernel.logs_dir%/%kernel.environment%.log + +services: + test_command_handler: + class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommandHandler + arguments: + - '@doctrine.orm.default_entity_manager' + tags: + - { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestCommand } + + some_other_test_command_handler: + class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommandHandler + arguments: + - '@event_recorder' + tags: + - { name: command_handler, handles: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherTestCommand } + + test_event_subscriber: + class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\TestEntityCreatedEventSubscriber + tags: + - { name: event_subscriber, subscribes_to: test_entity_created } + arguments: + - '@command_bus' + + some_other_event_subscriber: + class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\SomeOtherEventSubscriber + tags: + - { name: event_subscriber, subscribes_to: some_other_event } + arguments: + - '@command_bus' + +command_bus: + command_name_resolver_strategy: class_based + logging: ~ + +event_bus: + event_name_resolver_strategy: named_message + logging: ~ + +monolog: + handlers: + main: + type: stream + path: %log_file% + level: debug diff --git a/tests/Functional/SmokeTest/config2.yml b/tests/Functional/SmokeTest/config2.yml new file mode 100644 index 0000000..ab3c75b --- /dev/null +++ b/tests/Functional/SmokeTest/config2.yml @@ -0,0 +1,13 @@ +imports: + - { resource: config.yml } + +services: + auto_command_handler: + class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoCommandHandler + tags: + - { name: command_handler } + + auto_event_subscriber: + class: SimpleBus\SymfonyBridge\Tests\Functional\SmokeTest\Auto\AutoEventSubscriber + tags: + - { name: event_subscriber }