Skip to content

Commit fb62400

Browse files
committed
resolve bindings in aggregate handler
1 parent 40ba2df commit fb62400

File tree

5 files changed

+124
-69
lines changed

5 files changed

+124
-69
lines changed

src/DependencyInjection/HandlerCompilerPass.php

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,14 @@
44

55
namespace Patchlevel\EventSourcingBundle\DependencyInjection;
66

7-
use Patchlevel\EventSourcing\Attribute\Inject;
87
use Patchlevel\EventSourcing\CommandBus\Handler\CreateAggregateHandler;
9-
use Patchlevel\EventSourcing\CommandBus\Handler\ServiceNotResolvable;
108
use Patchlevel\EventSourcing\CommandBus\Handler\UpdateAggregateHandler;
119
use Patchlevel\EventSourcing\CommandBus\HandlerFinder;
1210
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootRegistry;
1311
use Patchlevel\EventSourcing\Repository\RepositoryManager;
14-
use Patchlevel\EventSourcingBundle\CommandBus\SymfonyParameterResolver;
15-
use ReflectionAttribute;
16-
use ReflectionMethod;
17-
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
18-
use Symfony\Component\DependencyInjection\Attribute\Autowire;
1912
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
2013
use Symfony\Component\DependencyInjection\ContainerBuilder;
2114
use Symfony\Component\DependencyInjection\Reference;
22-
use Symfony\Component\TypeInfo\Type\ObjectType;
23-
use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
2415

2516
use function sprintf;
2617
use function strtolower;
@@ -41,14 +32,11 @@ public function process(ContainerBuilder $container): void
4132

4233
foreach ($aggregateRootRegistry->aggregateClasses() as $aggregateName => $aggregateClass) {
4334
$parameterResolverId = sprintf('.event_sourcing.handler_parameter_resolver.%s', $aggregateName);
44-
$services = [];
4535

4636
foreach (HandlerFinder::findInClass($aggregateClass) as $aggregateHandler) {
4737
$handlerId = strtolower(sprintf('event_sourcing.handler.%s.%s', $aggregateName, $aggregateHandler->method));
4838
$handlerClass = $aggregateHandler->static ? CreateAggregateHandler::class : UpdateAggregateHandler::class;
4939

50-
$services += $this->services(new ReflectionMethod($aggregateClass, $aggregateHandler->method));
51-
5240
$container->register($handlerId, $handlerClass)
5341
->setArguments([
5442
new Reference(RepositoryManager::class),
@@ -61,61 +49,6 @@ public function process(ContainerBuilder $container): void
6149
'bus' => $bus,
6250
]);
6351
}
64-
65-
$container->register($parameterResolverId, SymfonyParameterResolver::class)
66-
->setArguments([
67-
new ServiceLocatorArgument($services),
68-
]);
6952
}
7053
}
71-
72-
/** @return array<string, mixed> */
73-
private function services(ReflectionMethod $method): array
74-
{
75-
$services = [];
76-
$prefix = strtolower($method->getName()) . '.';
77-
78-
foreach ($method->getParameters() as $index => $parameter) {
79-
if ($index === 0) {
80-
continue; // skip first parameter (command)
81-
}
82-
83-
$key = $prefix . $parameter->getName();
84-
85-
$attributes = $parameter->getAttributes(Inject::class);
86-
87-
if ($attributes !== []) {
88-
$services[$key] = new Reference($attributes[0]->newInstance()->service);
89-
90-
continue;
91-
}
92-
93-
$attributes = $parameter->getAttributes(Autowire::class, ReflectionAttribute::IS_INSTANCEOF);
94-
95-
if ($attributes !== []) {
96-
$services[$key] = $attributes[0]->newInstance()->value;
97-
98-
continue;
99-
}
100-
101-
$reflectionType = $parameter->getType();
102-
103-
if ($reflectionType === null) {
104-
throw ServiceNotResolvable::missingType($method->getDeclaringClass()->getName(), $parameter->getName());
105-
}
106-
107-
$type = TypeResolver::create()->resolve($reflectionType);
108-
109-
if (!$type instanceof ObjectType) {
110-
throw ServiceNotResolvable::typeNotObject(
111-
$method->getDeclaringClass()->getName(),
112-
$parameter->getName(),
113-
);
114-
}
115-
116-
$services[$key] = new Reference($type->getClassName());
117-
}
118-
119-
return $services;
120-
}
12154
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcingBundle\DependencyInjection;
6+
7+
use Patchlevel\EventSourcing\Attribute\Inject;
8+
use Patchlevel\EventSourcing\CommandBus\Handler\ServiceNotResolvable;
9+
use Patchlevel\EventSourcing\CommandBus\HandlerFinder;
10+
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootRegistry;
11+
use Patchlevel\EventSourcingBundle\CommandBus\SymfonyParameterResolver;
12+
use Psr\Container\ContainerInterface;
13+
use ReflectionAttribute;
14+
use ReflectionMethod;
15+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
16+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
17+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
18+
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\DependencyInjection\Reference;
20+
use Symfony\Component\TypeInfo\Type\ObjectType;
21+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
22+
23+
use function sprintf;
24+
use function strtolower;
25+
26+
/** @internal */
27+
final class HandlerServiceLocatorCompilerPass implements CompilerPassInterface
28+
{
29+
public function process(ContainerBuilder $container): void
30+
{
31+
if (!$container->hasParameter('patchlevel_event_sourcing.aggregate_handlers.bus')) {
32+
return;
33+
}
34+
35+
/** @var AggregateRootRegistry $aggregateRootRegistry */
36+
$aggregateRootRegistry = $container->get(AggregateRootRegistry::class);
37+
38+
foreach ($aggregateRootRegistry->aggregateClasses() as $aggregateName => $aggregateClass) {
39+
$parameterResolverId = sprintf('.event_sourcing.handler_parameter_resolver.%s', $aggregateName);
40+
$services = [];
41+
42+
foreach (HandlerFinder::findInClass($aggregateClass) as $aggregateHandler) {
43+
$services += $this->services(new ReflectionMethod($aggregateClass, $aggregateHandler->method), $container);
44+
}
45+
46+
$container->register($parameterResolverId, SymfonyParameterResolver::class)
47+
->setArguments([
48+
new ServiceLocatorArgument($services),
49+
]);
50+
}
51+
}
52+
53+
/** @return array<string, mixed> */
54+
private function services(ReflectionMethod $method, ContainerInterface $container): array
55+
{
56+
$services = [];
57+
$prefix = strtolower($method->getName()) . '.';
58+
59+
foreach ($method->getParameters() as $index => $parameter) {
60+
if ($index === 0) {
61+
continue; // skip first parameter (command)
62+
}
63+
64+
$key = $prefix . $parameter->getName();
65+
66+
$attributes = $parameter->getAttributes(Inject::class);
67+
68+
if ($attributes !== []) {
69+
$services[$key] = new Reference($attributes[0]->newInstance()->service);
70+
71+
continue;
72+
}
73+
74+
$attributes = $parameter->getAttributes(Autowire::class, ReflectionAttribute::IS_INSTANCEOF);
75+
76+
if ($attributes !== []) {
77+
$services[$key] = $attributes[0]->newInstance()->value;
78+
79+
continue;
80+
}
81+
82+
$reflectionType = $parameter->getType();
83+
84+
if ($reflectionType === null) {
85+
throw ServiceNotResolvable::missingType($method->getDeclaringClass()->getName(), $parameter->getName());
86+
}
87+
88+
$type = TypeResolver::create()->resolve($reflectionType);
89+
90+
if (!$type instanceof ObjectType) {
91+
throw ServiceNotResolvable::typeNotObject(
92+
$method->getDeclaringClass()->getName(),
93+
$parameter->getName(),
94+
);
95+
}
96+
97+
$binding = sprintf('%s $%s', $type->getClassName(), $parameter->getName());
98+
99+
if ($container->has($binding)) {
100+
$services[$key] = new Reference($binding);
101+
102+
continue;
103+
}
104+
105+
$services[$key] = new Reference($type->getClassName());
106+
}
107+
108+
return $services;
109+
}
110+
}

src/PatchlevelEventSourcingBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Patchlevel\EventSourcingBundle;
66

77
use Patchlevel\EventSourcingBundle\DependencyInjection\HandlerCompilerPass;
8+
use Patchlevel\EventSourcingBundle\DependencyInjection\HandlerServiceLocatorCompilerPass;
89
use Patchlevel\EventSourcingBundle\DependencyInjection\RepositoryCompilerPass;
910
use Patchlevel\EventSourcingBundle\DependencyInjection\SubscriberGuardCompilePass;
1011
use Patchlevel\EventSourcingBundle\DependencyInjection\TranslatorCompilerPass;
@@ -18,6 +19,7 @@ public function build(ContainerBuilder $container): void
1819
$container->addCompilerPass(new RepositoryCompilerPass());
1920
$container->addCompilerPass(new SubscriberGuardCompilePass());
2021
$container->addCompilerPass(new HandlerCompilerPass(), priority: 100);
22+
$container->addCompilerPass(new HandlerServiceLocatorCompilerPass(), priority: -100);
2123
$container->addCompilerPass(new TranslatorCompilerPass());
2224
}
2325
}

tests/Fixtures/Profile.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Patchlevel\EventSourcing\Attribute\Apply;
99
use Patchlevel\EventSourcing\Attribute\Handle;
1010
use Patchlevel\EventSourcing\Attribute\Id;
11+
use Patchlevel\EventSourcing\Repository\Repository;
1112

1213
#[Aggregate('profile')]
1314
class Profile extends BasicAggregateRoot
@@ -16,7 +17,10 @@ class Profile extends BasicAggregateRoot
1617
private CustomId $id;
1718

1819
#[Handle]
19-
public static function create(CreateProfile $command): self
20+
public static function create(
21+
CreateProfile $command,
22+
Repository $profileRepository
23+
): self
2024
{
2125
$profile = new self();
2226
$profile->recordThat(new ProfileCreated($command->id));

tests/Unit/PatchlevelEventSourcingBundleTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Doctrine\Migrations\Tools\Console\Command\MigrateCommand;
1010
use Doctrine\Migrations\Tools\Console\Command\StatusCommand;
1111
use InvalidArgumentException;
12+
use Patchlevel\EventSourcing\Aggregate\CustomId;
1213
use Patchlevel\EventSourcing\Clock\FrozenClock;
1314
use Patchlevel\EventSourcing\Clock\SystemClock;
1415
use Patchlevel\EventSourcing\CommandBus\Handler\CreateAggregateHandler;
@@ -92,6 +93,7 @@
9293
use Psr\EventDispatcher\EventDispatcherInterface;
9394
use Psr\Log\LoggerInterface;
9495
use Psr\SimpleCache\CacheInterface;
96+
use stdClass;
9597
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
9698
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
9799
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -526,7 +528,11 @@ public function testCommandHandler(): void
526528
]
527529
);
528530

529-
self::assertInstanceOf(CreateAggregateHandler::class, $container->get('event_sourcing.handler.profile.create'));
531+
$handler = $container->get('event_sourcing.handler.profile.create');
532+
533+
self::assertInstanceOf(CreateAggregateHandler::class, $handler);
534+
535+
$handler(new CreateProfile(CustomId::fromString('1')));
530536

531537
$definition = $container->getDefinition('event_sourcing.handler.profile.create');
532538
$tags = $definition->getTag('messenger.message_handler');

0 commit comments

Comments
 (0)