【问题标题】:ZF3 Dependency injection in Module.phpModule.php 中的 ZF3 依赖注入
【发布时间】:2019-02-15 03:10:56
【问题描述】:

我目前正在将 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 直接移动到 Controller 并将 ACL 注入其中?不知道这样做的正确方法是什么。

对此的任何反馈将不胜感激。

问候,

罗伯特

【问题讨论】:

    标签: php zend-framework3


    【解决方案1】:

    要解决这个问题,您应该使用 Listener 类和 Factory。它还可以帮助您更好地分离关注点:)

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


    在你的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;
        }
    
    }
    

    重要的部分:

    • Listener必须实现 ListenerAggregateInterface
    • 必须在模块配置的listeners 键中激活

    真的是这样。然后,您就有了 Listener 的基本构建块。

    除了attach 函数之外,如果您愿意,您可以将其余部分放入抽象类中。将使用多个侦听器节省几行(阅读:重复代码)。


    注意:上面的例子使用的是普通的EventManager。通过对上述代码进行简单更改,您可以创建“通用”侦听器,方法是将它们附加到 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) {...}
    

    【讨论】:

    • 非常感谢,这是一个了不起的回应。我明天会测试它,但我相信这正是我需要的! :-)
    • 再次感谢您,效果很好。我唯一遇到的问题是将优先级设置为 1000,这使我的事件发生时没有 RouteMatched。将其设置回默认值 1,其他一切顺利。
    • 是的,这只是你可以给自己的优先级。对我来说是ActiveSessionListener。早期我想知道是谁在使用我的应用程序,所以它是在这个优先级的早期触发的。在优先级为 999 时,我得到了一个授权侦听器,以确保确定优先级为 1000 的用户被检查以允许在路由上使用;-)
    猜你喜欢
    • 1970-01-01
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 2014-06-12
    • 2013-04-10
    相关资源
    最近更新 更多