1<?php
2
3namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
4
5use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6use Symfony\Component\DependencyInjection\ContainerBuilder;
7use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
8use Symfony\Component\DependencyInjection\Reference;
9
10/**
11 * Class for Symfony bundles to register entity listeners
12 */
13class EntityListenerPass implements CompilerPassInterface
14{
15    /**
16     * {@inheritDoc}
17     */
18    public function process(ContainerBuilder $container)
19    {
20        $resolvers = $container->findTaggedServiceIds('doctrine.orm.entity_listener');
21
22        foreach ($resolvers as $id => $tagAttributes) {
23            foreach ($tagAttributes as $attributes) {
24                $name          = isset($attributes['entity_manager']) ? $attributes['entity_manager'] : $container->getParameter('doctrine.default_entity_manager');
25                $entityManager = sprintf('doctrine.orm.%s_entity_manager', $name);
26
27                if (! $container->hasDefinition($entityManager)) {
28                    continue;
29                }
30
31                $resolverId = sprintf('doctrine.orm.%s_entity_listener_resolver', $name);
32
33                if (! $container->has($resolverId)) {
34                    continue;
35                }
36
37                $resolver = $container->findDefinition($resolverId);
38                $resolver->setPublic(true);
39
40                if (isset($attributes['entity']) && isset($attributes['event'])) {
41                    $this->attachToListener($container, $name, $id, $attributes);
42                }
43
44                if (isset($attributes['lazy']) && $attributes['lazy']) {
45                    $listener = $container->findDefinition($id);
46
47                    if ($listener->isAbstract()) {
48                        throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as this entity listener is lazy-loaded.', $id));
49                    }
50
51                    $interface = 'Doctrine\\Bundle\\DoctrineBundle\\Mapping\\EntityListenerServiceResolver';
52                    $class     = $resolver->getClass();
53
54                    if (substr($class, 0, 1) === '%') {
55                        // resolve container parameter first
56                        $class = $container->getParameterBag()->resolveValue($resolver->getClass());
57                    }
58
59                    if (! is_a($class, $interface, true)) {
60                        throw new InvalidArgumentException(
61                            sprintf('Lazy-loaded entity listeners can only be resolved by a resolver implementing %s.', $interface)
62                        );
63                    }
64
65                    $listener->setPublic(true);
66
67                    $resolver->addMethodCall('registerService', [$listener->getClass(), $id]);
68                } else {
69                    $resolver->addMethodCall('register', [new Reference($id)]);
70                }
71            }
72        }
73    }
74
75    private function attachToListener(ContainerBuilder $container, $name, $id, array $attributes)
76    {
77        $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
78
79        if (! $container->has($listenerId)) {
80            return;
81        }
82
83        $serviceDef = $container->getDefinition($id);
84
85        $args = [
86            $attributes['entity'],
87            $serviceDef->getClass(),
88            $attributes['event'],
89        ];
90
91        if (isset($attributes['method'])) {
92            $args[] = $attributes['method'];
93        }
94
95        $container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
96    }
97}
98