Search code examples
zend-frameworkdependency-injectionzend-framework3

ZF3: SharedEventManager injection in EventManager


In ZF2, an event was shared across multiple controllers using a shared event manager

Module.php

use Zend\Mvc\MvcEvent;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    public function onBootstrap(MvcEvent $event)
    {
        $services = $event->getApplication()->getServiceManager();
        $sharedEventManager = $eventManager->getSharedManager();

        $sharedEventManager->attach('user', 'log-fail', function($event) use ($services) {
            $username = $event->getParam('username');
            $log = $services->get('log');
            $log->warn('Error logging user: ' . $username);
        });
    }
}

LogController.php

<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\EventManager\EventManager;

class LogController extends AbstractActionController
{
    public function __construct()
    {
    }

    public function inAction()
    {
        //...
        if (! $isValid) {
            $event = new EventManager('user');
            $event->trigger('log-fail', $this, array('username'=> $username));
            //...
        }
    }
}

But ZF3 requires that the shared event manager be injected at instantiation, instead of via a setter. https://docs.zendframework.com/zend-eventmanager/migration/changed/

So I created a log controller factory to inject the shared event manager but I can't find how to do the injection

LogControllerFactory.php

<?php

namespace Application\Controller;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;

class LogControllerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $sharedEventManager = ???

        return new LogController($sharedEventManager);
    }
}

Would you have any idea on how to get the shared event manager instanced to inject it in the controller in order to get it in the controller __construct function?


Solution

  • To answer my own question:

    1. config.php

    The config file

    /..
    'controllers' => [
        'factories' => [
            Controller\LogController::class => Controller\LogControllerFactory::class,
            /..
        ],
        'aliases' => [
            'log' => Controller\LogController::class,
            /..
        ]
    ],
    /..
    'service_manager' => [
        'factories' => [
            'log' => Service\Log::class,
        ],
    ],
    /..
    

    2. Module.php

    The module file

    namespace Application;
    
    use Zend\Mvc\MvcEvent;
    
    class Module
    {
        public function getConfig()
        {
            return include __DIR__ . '/../config/module.config.php';
        }
    
        public function onBootstrap(MvcEvent $event)
        {
            // Get shared service manager
            $services = $event->getApplication()->getServiceManager();
            $sharedEventManager = $eventManager->getSharedManager();
    
            // Attach listener
            $sharedEventManager->attach('user', 'log-fail', function($event) use ($services) {
                $username = $event->getParam('username');
                $log = $services->get('log');
                $log->warn('Error logging user: ' . $username);
            });
        }
    }
    

    3. LogService.php

    The service file

    namespace Application\Service;
    
    use Interop\Container\ContainerInterface;
    use Zend\ServiceManager\Factory\FactoryInterface;
    use Zend\ServiceManager\ServiceLocatorInterface;
    use Zend\Log\Logger;
    use Zend\Log\Writer\Stream as StreamWriter;
    use Zend\Log\Filter\Priority as PriorityFilter;
    
    class Log implements FactoryInterface
    {
        public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
        {
            $controllerPluginManager = $container;
            $serviceManager = $controllerPluginManager->get('ServiceManager');
    
            // Instantiate Zend\Log\Logger
            $log = new Logger();
            // Set stream to write in a log file
            $writer = new StreamWriter('data/logs/audit.log');
            $log->addWriter($writer);
    
            return $log;
        }
    }
    

    4. LogControllerFactory.php*

    The controller factory file

    namespace Application\Controller;
    
    use Interop\Container\ContainerInterface;
    use Zend\ServiceManager\Factory\FactoryInterface;
    
    class LogControllerFactory implements FactoryInterface
    {
        public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
        {
            $controllerPluginManager = $container;
            $eventManager = $controllerPluginManager->get('EventManager');
            $sharedEventManager = $eventManager->getSharedManager();
    
            return new LogController($sharedEventManager);
        }
    }
    

    5. LogController.php

    The controller file

    namespace Application\Controller;
    
    use Zend\Mvc\Controller\AbstractActionController;
    use Zend\EventManager\EventManager;
    use Zend\EventManager\SharedEventManager;
    
    class LogController extends AbstractActionController
    {
        public function __construct(SharedEventManager $sharedEventManager)
        {
            $this->_sharedEventManager = $sharedEventManager;
        }
    
        public function inAction()
        {
            //...
            if (! $isValid) {
                $event = new EventManager($this->_sharedEventManager, ['user']);
                $event->trigger('log-fail', $this, array('username'=> $username));
                //...
            }
        }
    }