doctrine/event-manager

removeEventListener method can't remove listeners registered with a different id than spl object hash

n-richaud-P2S opened this issue · 8 comments

Some bundle use the same class as listener and register listener id are not based on object hash
Example with FriendsOfSymfony/FOSElasticaBundle :
Listener Id are generated like this : fos_elastica.listener.%indexName%.%typeName%
So listener registered by this bundle cannot be removed

Any solution for this problem?

The listeners can be removed normally as far I can say: each service under fos_elastica.listener.%indexName%.%typeName% name is a different instance of a listener class thus they have different object ids. We're often disabling FOSElastica's listeners for batch operations (and even re-enabling them later) in my work-app. The disabling goes like this:

    public static function disableFosElasticaEvents(EntityManagerInterface $entityManager): array
    {
        $eventManager = $entityManager->getEventManager();

        $removedEvents = [];

        foreach ($eventManager->getListeners() as $event => $listeners) {
            foreach ($listeners as $key => $listener) {
                if ($listener instanceof FOSElasticaListener) {
                    $removedEvents[$event][] = $listener;
                    $eventManager->removeEventListener($event, $listener);
                }
            }
        }

        return $removedEvents;
    }

@malarzm What do you call this method? FOSElasticaListener is FOS\ElasticaBundle\Doctrine\Listener?

FOSElasticaListener is FOS\ElasticaBundle\Doctrine\Listener?

Yes it is, sorry for not expanding an alias (we have some other helper functions to disable other listeners also named as Listener hence the alias)

I have a similar code in a Command, but it does not eliminate the listeners:

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $evm = $this->em->getEventManager();
        foreach ($evm->getListeners() as $event => $listeners) {
            foreach ($listeners as $listener) {
                if ($listener instanceof Listener) {
                    $evm->removeEventListener([$event], $listener);
                }
            }
        }

        dump($evm->getListeners());
...

dump:

....
  "postFlush" => array:2 [
    "000000006b5c0425000000003bd4896f" => Gedmo\Sortable\SortableListener^ {#633}
    "_service_fos_elastica.listener.persona.persona" => FOS\ElasticaBundle\Doctrine\Listener^ {#614}
  ]

What is the use of return removedEvents in your code?

What is the use of return removedEvents in your code?

After the batch operation is finished we add the listeners back so the state of the app is the same as before.

@malarzm Thank you for your answers. But this code doesn't work in my case. The listeners are active after "remove them"

This solution does not work anymore. Since 2.0.0 there is a type check in removeEventListener: v1.0.0...2.0.0#diff-44578590b0e7c514045f23763933cdf7f6f29a0c0972ffcab6529acabde50224R98

How should we handle such case?