1515use Symfony \Component \DependencyInjection \ChildDefinition ;
1616use Symfony \Component \DependencyInjection \Compiler \CompilerPassInterface ;
1717use Symfony \Component \DependencyInjection \ContainerBuilder ;
18+ use Symfony \Component \DependencyInjection \Definition ;
1819use Symfony \Component \DependencyInjection \Exception \LogicException ;
1920use Symfony \Component \DependencyInjection \Loader \Configurator \ContainerConfigurator ;
2021use Symfony \Component \DependencyInjection \Reference ;
2122use Symfony \Component \HttpKernel \Bundle \AbstractBundle ;
23+ use Zenstruck \Foundry \Attribute \AsFoundryHook ;
2224use Zenstruck \Foundry \Attribute \AsFixture ;
2325use Zenstruck \Foundry \DependencyInjection \AsFixtureStoryCompilerPass ;
2426use Zenstruck \Foundry \InMemory \DependencyInjection \InMemoryCompilerPass ;
2527use Zenstruck \Foundry \InMemory \InMemoryRepository ;
2628use Zenstruck \Foundry \Mongo \MongoResetter ;
29+ use Zenstruck \Foundry \Object \Event \Event ;
30+ use Zenstruck \Foundry \Object \Event \HookListenerFilter ;
2731use Zenstruck \Foundry \Object \Instantiator ;
2832use Zenstruck \Foundry \ORM \ResetDatabase \MigrateDatabaseResetter ;
2933use Zenstruck \Foundry \ORM \ResetDatabase \OrmResetter ;
@@ -238,6 +242,25 @@ public function loadExtension(array $config, ContainerConfigurator $configurator
238242 $ this ->configureInMemory ($ configurator , $ container );
239243 $ this ->configureFixturesStory ($ container );
240244 $ this ->configureAutoRefreshWithLazyObjects ($ container , $ config ['enable_auto_refresh_with_lazy_objects ' ] ?? null );
245+
246+ $ container ->registerAttributeForAutoconfiguration (
247+ AsFoundryHook::class,
248+ // @phpstan-ignore argument.type
249+ static function (ChildDefinition $ definition , AsFoundryHook $ attribute , \ReflectionMethod $ reflector ) {
250+ if (1 !== \count ($ reflector ->getParameters ())
251+ || !$ reflector ->getParameters ()[0 ]->getType ()
252+ || !$ reflector ->getParameters ()[0 ]->getType () instanceof \ReflectionNamedType
253+ || !\is_a ($ reflector ->getParameters ()[0 ]->getType ()->getName (), Event::class, true )
254+ ) {
255+ throw new LogicException (\sprintf ("In order to use \"%s \" attribute, method \"{$ reflector ->class }:: {$ reflector ->name }() \" must have a single parameter that is a subclass of \"%s \". " , AsFoundryHook::class, Event::class));
256+ }
257+ $ definition ->addTag ('foundry.hook ' , [
258+ 'class ' => $ attribute ->objectClass ,
259+ 'method ' => $ reflector ->getName (),
260+ 'event ' => $ reflector ->getParameters ()[0 ]->getType ()->getName (),
261+ ]);
262+ }
263+ );
241264 }
242265
243266 public function build (ContainerBuilder $ container ): void
@@ -258,6 +281,21 @@ public function process(ContainerBuilder $container): void
258281 ->addMethodCall ('addProvider ' , [new Reference ($ id )])
259282 ;
260283 }
284+
285+ // events
286+ $ i = 0 ;
287+ foreach ($ container ->findTaggedServiceIds ('foundry.hook ' ) as $ id => $ tags ) {
288+ foreach ($ tags as $ tag ) {
289+ $ container
290+ ->setDefinition ("foundry.hook. {$ tag ['event ' ]}. {$ i }" , new Definition (class: HookListenerFilter::class))
291+ ->setArgument (0 , [new Reference ($ id ), $ tag ['method ' ]])
292+ ->setArgument (1 , $ tag ['class ' ])
293+ ->addTag ('kernel.event_listener ' , ['event ' => $ tag ['event ' ]])
294+ ;
295+
296+ ++$ i ;
297+ }
298+ }
261299 }
262300
263301 /**
0 commit comments