Module.php 中的 ZF3 依赖注入

ZF3 Dependency injection in Module.php

我目前正在将 ZF2 应用程序迁移到 ZF3。 大多数情况下一切都很顺利,但我坚持一件事。

在我的 Module.php 中,我有一个使用 zend-permissions-acl 的 ACL 管理。

class Module
{
protected $defaultLang = 'fr';

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

    if (!$e->getRequest() instanceof ConsoleRequest){
        $eventManager->attach(MvcEvent::EVENT_RENDER_ERROR, array($this, 'onRenderError'));
        $eventManager->attach(MvcEvent::EVENT_RENDER, array($this, 'onRender'));
        $eventManager->attach(MvcEvent::EVENT_FINISH, array($this, 'onFinish'));

        $this->initAcl($e);
        $eventManager->attach('route', array($this, 'checkAcl'));
    }
}

public function checkAcl(MvcEvent $e) {
    $app = $e->getApplication();
    $sm  = $app->getServiceManager();
    $route = $e -> getRouteMatch() -> getMatchedRouteName();
    $authService = $sm->get('AuthenticationService');
    $jwtService = $sm->get('JwtService');
    $translator = $sm->get('translator');

    $identity = null;
    try {
        $identity = $jwtService->getIdentity($e->getRequest());
    } catch(\Firebase\JWT\ExpiredException $exception) {
        $response = $e->getResponse();

        $response->setStatusCode(401);
        return $response;
    }

    if(is_null($identity) && $authService->hasIdentity()) { // no header being passed on... we try to use standard validation
        $authService->setJwtMode(false);
        $identity = $authService->getIdentity();
    }

    $userRole = 'default';
    $translator->setLocale($this->defaultLang);
    if(!is_null($identity))
    {
        $userRole = $identity->getType();

        //check if client or prospect
        if($userRole >= User::TYPE_CLIENT)
        {
            $userManagementRight = UserRight::CREATE_USERS;
            if($identity->hasRight($userManagementRight))
                $userRole = 'userManagement';
        }

        $translator->setLocale($identity->getLang());
    }

    if (!$e->getViewModel()->acl->isAllowed($userRole, null, $route)) {
        $response = $e -> getResponse();

        $response->setStatusCode(403);
        return $response;
    }
public function initAcl(MvcEvent $e) {
    //here is list of routes allowed
}
}

我的问题是我仍在使用 getServiceManager,因此收到已弃用的警告:Usage of Zend\ServiceManager\ServiceManager::getServiceLocator is deprecated since v3.0.0;

基本上,我只需要将依赖项注入Module.php。 我想否则我必须将 checkAcl 直接移动到控制器并在其中注入 ACL 吗?不确定这样做的正确方法是什么。

如有任何反馈,我们将不胜感激。

此致,

罗伯特

要解决此问题,您应该使用监听器 class 和工厂。它还可以帮助您更好地分离关注点 :)

从你的代码来看,你似乎很有能力解决问题。因此,我只是给你一个我自己的例子,所以你应该用你自己的代码填写你的例子(我也有点懒惰,不想重写一切 copy/paste我的代码在 ;) )


在你的module.config.php中:

'listeners'       => [
    // Listing class here will automatically have them "activated" as listeners
    ActiveSessionListener::class,
],
'service_manager' => [
    'factories' => [
        // The class (might need a) Factory
        ActiveSessionListener::class => ActiveSessionListenerFactory::class,
    ],
],

工厂

<?php

namespace User\Factory\Listener;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManager;
use Interop\Container\ContainerInterface;
use User\Listener\ActiveSessionListener;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;

class ActiveSessionListenerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        /** @var ObjectManager $entityManager */
        $entityManager = $container->get(EntityManager::class);
        /** @var AuthenticationService $authenticationService */
        $authenticationService = $container->get(AuthenticationService::class);

        return new ActiveSessionListener($authenticationService, $entityManager);
    }
}

聆听者

<?php

namespace User\Listener;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManager;
use User\Entity\User;
use Zend\Authentication\AuthenticationService;
use Zend\EventManager\Event;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\Mvc\MvcEvent;

/**
 * Class ActiveSessionListener
 *
 * @package User\Listener
 *
 * Purpose of this class is to make sure that the identity of an active session becomes managed by the EntityManager.
 * A User Entity must be in a managed state in the event of any changes to the Entity itself or in relations to/from it.
 */
class ActiveSessionListener implements ListenerAggregateInterface
{
    /**
     * @var AuthenticationService
     */
    protected $authenticationService;

    /**
     * @var ObjectManager|EntityManager
     */
    protected $objectManager;

    /**
     * @var array
     */
    protected $listeners = [];

    /**
     * CreatedByUserListener constructor.
     *
     * @param AuthenticationService $authenticationService
     * @param ObjectManager         $objectManager
     */
    public function __construct(AuthenticationService $authenticationService, ObjectManager $objectManager)
    {
        $this->setAuthenticationService($authenticationService);
        $this->setObjectManager($objectManager);
    }

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

    /**
     * @param EventManagerInterface $events
     */
    public function attach(EventManagerInterface $events, $priority = 1)
    {
        $events->attach(MvcEvent::EVENT_ROUTE, [$this, 'haveDoctrineManagerUser'], 1000);
    }

    /**
     * @param Event $event
     *
     * @throws \Doctrine\Common\Persistence\Mapping\MappingException
     * @throws \Doctrine\ORM\ORMException
     */
    public function haveDoctrineManagerUser(Event $event)
    {
        if ($this->getAuthenticationService()->hasIdentity()) {
            // Get current unmanaged (by Doctrine) session User
            $identity = $this->getAuthenticationService()->getIdentity();

            // Merge back into a managed state
            $this->getObjectManager()->merge($identity);
            $this->getObjectManager()->clear();

            // Get the now managed Entity & replace the unmanaged session User by the managed User
            $this->getAuthenticationService()->getStorage()->write(
                $this->getObjectManager()->find(User::class, $identity->getId())
            );
        }
    }

    /**
     * @return AuthenticationService
     */
    public function getAuthenticationService() : AuthenticationService
    {
        return $this->authenticationService;
    }

    /**
     * @param AuthenticationService $authenticationService
     *
     * @return ActiveSessionListener
     */
    public function setAuthenticationService(AuthenticationService $authenticationService) : ActiveSessionListener
    {
        $this->authenticationService = $authenticationService;

        return $this;
    }

    /**
     * @return ObjectManager|EntityManager
     */
    public function getObjectManager()
    {
        return $this->objectManager;
    }

    /**
     * @param ObjectManager|EntityManager $objectManager
     *
     * @return ActiveSessionListener
     */
    public function setObjectManager($objectManager)
    {
        $this->objectManager = $objectManager;

        return $this;
    }

}

重要的部分:

  • Listenerclass必须执行ListenerAggregateInterface
  • 必须在模块配置的listeners激活

确实如此。然后,您就拥有了监听器的基本构建块。

除了 attach 函数之外,如果您愿意,您可以将其余的函数变成抽象的 class。使用多个侦听器可以节省几行代码(读作:重复代码)。


注意: 以上示例使用正常的 EventManager。通过对上述代码进行简单的更改,您可以创建 "generic" 个侦听器,方法是将它们附加到 SharedEventManager,如下所示:

/**
 * @param EventManagerInterface $events
 */
public function attach(EventManagerInterface $events, $priority = 1)
{
    $sharedManager = $events->getSharedManager();

    $sharedManager->attach(SomeClass::class, EventConstantClass::SOME_STRING_CONSTANT, [$this, 'callbackFunction']);
}

public function callbackFunction (MvcEvent $event) {...}