Alwin Kesler
Alwin Kesler

Reputation: 1520

ZF2 detach listener from EventManager

Implementing a new ZF2 app I found myself struggling with the EventManager. In the application module I created an AuthenticationListener and a NotifierListener. The first checks for proper authentication on users and the second displays custom messages addressed to the logged user.

Now I'm creating a new module that dispatches xml files to export. I want to keep the AuthenticationListener but detach the NotifierListener to avoid E_NOTIFY errors. The problem is that after reading blogs/source code/documentation (pretty poor) and pulling my hair out I'm unable to understand how to detach the listener mentioned above.

Application module.php

public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();

    $authListener = new AuthenticationListener();
    $authListener->attach($eventManager);

    $notifyListener = new NotifyListener();
    $notifyListener->attach($eventManager);
}

New module.php

public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();

    // ... $eventmanager->detach('NotifyListener');
}

Upvotes: 1

Views: 1610

Answers (2)

Crisp
Crisp

Reputation: 11447

The answer is to make your listener a service

Since it requires no constructor params, it's invokable, so set it in the invokables array

public function getServiceConfig()
{
    return array(
        'invokables' => array(
            'NotifyListener' => 'FQCN\To\NotifyListener',
        ),
    );
}

Fetch it from the service manager instead of creating a new instance directly in your bootstrap

public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();

    $sm = $e->getApplication()->getServiceManager();

    $notifyListener = $sm->get('NotifyListener')
    $notifyListener->attach($eventManager);
}

Anywhere you want to detach it, you just need to be able to get at the ServiceManager

 // in any ServiceLocator aware class
 $sm = $this->getServiceLocator();
 $eventManager = $sm->get('Application')->getEventManager();
 $notifyListener = $sm->get('NotifyListener');
 $notifyListener->detach($eventManager);

Upvotes: 3

Stoyan Dimov
Stoyan Dimov

Reputation: 5539

You are on the right path. The only thing that you need to do is to call detach() on your NotifyListener and not on the EvenManager. The example bellow comes from the Zend Framework manual for the EventManager.

link: http://framework.zend.com/manual/2.0/en/modules/zend.event-manager.event-manager.html#examples

use Zend\Cache\Cache;
use Zend\EventManager\EventCollection;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\EventInterface;

class CacheListener implements ListenerAggregateInterface
{
    protected $cache;

    protected $listeners = array();

    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }

    public function attach(EventCollection $events)
    {
        $this->listeners[] = $events->attach('get.pre', array($this, 'load'), 100);
        $this->listeners[] = $events->attach('get.post', array($this, 'save'), -100);
    }

    public function detach(EventManagerInterface $events)
    {
        foreach ($this->listeners as $index => $listener) {
            if ($events->detach($listener)) {
                unset($this->listeners[$index]);
            }
        }
    }

    public function load(EventInterface $e)
    {
        // some code to load
    }

    public function save(EventInterface $e)
    {
        // some code to save 
    }
}

This example illustrates very well how to prepare a listener (e.g. AuthenticationListener) implementing ListenerAggregateInterface.

Assuming the same situation as with your Module object:

public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();
    $cacheListener = new CacheListener($cache);
    $cacheListener->detach($eventManager);
}

So, assuming your NotifyListener implements ListenerAggregateInterface or simply has a detach() method you can write your onBootstrap() method to detach the listener like this:

public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();
    $notifyListener = new NotifyListener();
    $notifyListener->detach($eventManager);
}

Hope this helps, feedback will be appreciated :)

Stoyan

Upvotes: 0

Related Questions