简体   繁体   中英

ZF2 events for multiple modules

Currently I have an ZF2 application configured with the single module "application". I bootstrap the application an attach an event this way:

namespace Application;

use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap( MvcEvent $e)
    {
        $eventManager        = $e->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach( $eventManager);

        $this->initTracking( $e);
    }

    /**
     * Initialises user tracking check
     * @param MvcEvent $e
     */
    public function initTracking( MvcEvent $e) 
    {
        $eventManager = $e->getApplication()->getEventManager();
        $eventManager->attach( 'dispatch', function( $e){ 
            $objTracking = new \Application\Event\Tracking( $e);
        }, 200);
    }
}

Now I need to create a new module "api", which should process only urls starting domain.com/api (I configure the router in "api" module config file to handle only such urls). I bootstrap the "api" module the same way as "application" module, and I attach a dedicated event:

namespace Api;

use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;

class Module
{
    public function onBootstrap( MvcEvent $e)
    {
        $eventManager        = $e->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach( $eventManager);

        $this->initLogging( $e);
    }

    /**
     * Initialises loggging
     * @param MvcEvent $e
     */
    public function initLogging( MvcEvent $e) 
    {
        $eventManager = $e->getApplication()->getEventManager();
        $eventManager->attach( 'dispatch', function( $e){ 
            $objLogger = new \Application\Event\Logging( $e);
        }, 200);
    }
}

What happens is that when I call domain.com/application - both modules are being initialised and events from both modules are being triggered. I need events to be triggered depending on the application which is dispatching the action.

How can I achieve that?

You are currently attaching the event listeners to the application event manager. This is a single event manager instance that will trigger all MVC events.

As it is the same instance it will make no difference where you attach the listeners; they will all be triggered regardless.

You will need to specifically check, in each listener, if the matched route is one that the listener should action. If it is not then exit out early.

For example:

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

    // There is no need to pass in the event 
    // to a seperate function as we can just attach 'initLogging' here
    // as the event listener
    $eventManager->attach('dispatch', array($this, 'initLogging'), 200);
}

// initLogging listener
public function initLogging(MvcEvent $event)
{
    //... check the route is one you want
    // this is quite basic to you might need to edit to
    // suit your specific needs 
    $routeName = $event->getRouteMatch()->getMatchedRouteName();

    if (false === strpos($routeName, 'api')) {
        // we are not an api route so exit early
        return;
    }
    $objLogger = new \Application\Event\Logging($event);
}

So the listener will still be triggered, however it won't 'do' anything.

You can however go further and prevent this unnecessary call by specifically targeting the required event manager that you are interested in; to do so we can use the SharedEventManager .

When attaching the listener to the SharedEventManager you need to provide an 'identifier' of the target event manager - I'll assume you are targeting a 'API controller'.

So the above would be changed to

public function onBootstrap(MvcEvent $event)
{
    $application = $event->getApplication();
    $sharedEventManager = $application->getEventManager()
                                      ->getSharedManager();

    // The shared event manager takes one additional argument,
    // 'Api\Controller\Index' is our target identifier
    $eventManager->attach('Api\Controller\Index', 'dispatch', array($this, 'initLogging'), 200);
}

// initLogging listener
public function initLogging(MvcEvent $event)
{
    // ... same bits we had before
}

the onDispatch method will be run in only one module

namespace Application;

use Zend\Http\PhpEnvironment\Request;
use Zend\Http\PhpEnvironment\Response;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ModuleManager\ModuleManagerInterface;
use Zend\Mvc\MvcEvent;

/**
 * @method Request getRequest()
 * @method Response getResponse()
 */
class Module implements ConfigProviderInterface
{
    public function getConfig()
    {
        return array_merge(
            require __DIR__ . '/../config/module.config.php',
            require __DIR__ . '/../config/router.config.php'
        );
    }

    public function init(ModuleManagerInterface $manager)
    {
        $eventManager = $manager->getEventManager();
        // Register the event listener method.
        $sharedEventManager = $eventManager->getSharedManager();
        $sharedEventManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH,
                                     [$this, 'onDispatch'], 100);
    }

    public function onDispatch(MvcEvent $e)
    {
        var_dump(__METHOD__);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM