phpzend-framework2laminas-api-tools

OnDispatch event in Service Constructor


I have problem with attaching OnDispatch event in Apigility. I want to have value from custom header in constructor of my abstract Service Class. It works when I simply add it to on bootstrap in Module.php

public function onBootstrap(MvcEvent $e)
{
    $eventManager        = $e->getApplication()->getEventManager();
    $moduleRouteListener = new ModuleRouteListener();
    $moduleRouteListener->attach($eventManager);
    $eventManager->attach(\Zend\Mvc\MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
}

public function onDispatch(\Zend\Mvc\MvcEvent $e){
    $e->getRequest()->getHeaders()->get('User-Token')->getFieldValue();
}

But I don't know how to pass this value to ServiceAbstract Constructor.

I also tried with implementing EventManagerAwareInterface and attaching events with attachDefaultListeners() method but that didn't get me any results - attached function was not called. What is the proper approach to attaching to events NOT in controllers? Thanks for any help.


Solution

  • It seems to me that you have two different goals here.

    1. Store a header variable from a route event.

    2. Get a dependency into the constructor function of a class

    To answer 1.

    You can make a listener class and attach this class to your EventManager. This would look something like this:

    <?php
    
    namespace My\Listener;
    
    use Zend\EventManager\ListenerAggregateInterface;
    use Zend\EventManager\EventManagerInterface;
    use Zend\Mvc\MvcEvent;
    use Zend\Http\Headers;
    use Zend\Http\Request as HttpRequest;
    
    class MyCustomListener implements ListenerAggregateInterface
    {
        /**
         * @var \Zend\Stdlib\CallbackHandler[]
         */
        protected $listeners = array();
    
        /**
         * @param EventManagerInterface $eventManager
         */
        public function attach(EventManagerInterface $eventManager)
        {
            // attach on route
            $this->listeners[] = $eventManager->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
        }
    
        /**
         * @param EventManagerInterface $eventManager
         */
        public function detach(EventManagerInterface $eventManager)
        {
            foreach ($this->listeners as $index => $listener) {
                if ($eventManager->detach($listener)) {
                    unset($this->listeners[$index]);
                }
            }
        }
    
        /**
         * Do your thing on dispatch event with your headers
         *
         * @param MvcEvent $event
         */
        public function onDispatch(MvcEvent $event)
        {
            $request = $event->getRequest();
            if(!$request instanceof HttpRequest){
                // Nothing to do
                return;
            }
    
            $headers = $request->getHeaders();
    
            // You could for example get a service here and store your value
        }
    }
    

    You attach this listener in Module.php like this:

    public function onBootstrap(MvcEvent $event)
    {
        $event->getApplication();
        $application->getEventManager();
        $eventManager->attach($serviceManager->get('My\Listener\MyCustomListener'));
    }
    

    You have to register your listener in your ServiceManager either under invokables or factories with the key My\Listener\MyCustomListener to be able to attach it here like this.

    To answer 2:

    To get a dependency in your constructor you can make a factory for your class and get the dependency from the service can provide the dependency that you need (could be from the listener from 1 directly).

    <?php
    namespace My\Factory;
    
    use Zend\ServiceManager\FactoryInterface;
    use Zend\ServiceManager\ServiceLocatorInterface;
    use My\Folder\MyCustomClass;
    
    class MyClassFactory implements FactoryInterface
    {
        /**
         * @param  ServiceLocatorInterface $serviceLocator
         * @return Logger
         */
        public function createService(ServiceLocatorInterface $serviceLocator)
        {
            $controllerPluginManager = $serviceLocator;
            $service = $serviceManager->get(My\Service\MyStorageService);
            $dependency = $service->getDependency();
            return new MyCustomClass($dependency);
        }
    }