简体   繁体   English

在Zend Framework 2中动态加载模块

[英]Loading Modules Dynamically in Zend Framework 2

I have asked this question yesterday as well, but this one includes code. 我昨天也问过这个问题,但这个问题包括代码。

Issue 问题

My application have multiple modules and 2 types of user accounts, Some modules are loaded always which are present in application.config.php some of them are conditional ie some are loaded for user type A and some for user type B 我的应用程序有多个模块和两种类型的用户帐户,一些模块always加载在application.config.php ,其中一些是conditional即一些加载用户type A ,一些加载用户type B

After going through documentations and questions on Stack Overflow, I understand some of ModuleManager functionalities and started implementing the logic that I though might work. 在浏览Stack Overflow上的文档和问题之后,我了解了一些ModuleManager功能,并开始实现我可能会工作的逻辑。

Some how I figured out a way to load the modules that are not present in application.config.php [SUCCESS] but their configuration is not working [THE ISSUE] ie if in onBootstrap method I get the ModuleManager service and do getLoadedModules() I get the list of all the modules correctly loaded. 一些我怎么想出一种方法来加载application.config.php [SUCCESS]中不存在的模块,但是它们的配置不起作用[问题]即如果在onBootstrap方法中我获得了ModuleManager服务并且执行了getLoadedModules()我获取正确加载的所有模块的列表。 Afterwards if I try to get some service from that dynamically loaded module, it throws exception. 之后如果我尝试从动态加载的模块中获取某些服务,它会抛出异常。

Zend\\ServiceManager\\ServiceManager::get was unable to fetch or create an instance for jobs_mapper Zend \\ ServiceManager \\ ServiceManager :: get无法获取或创建jobs_mapper的实例

Please note that, the factories and all other stuff are perfectly fine because if I load the module from application.config.php it works fine 请注意,工厂和所有其他东西都很好,因为如果我从application.config.php加载模块它工作正常

Similarly when I try to access any route from the dynamically loaded module it throws 404 Not Found which made it clear that the configuration from module.config.php of these modules are not loading even though the module is loaded by ModuleManager. 类似地,当我尝试从动态加载的模块访问任何路由时,它会抛出404 Not Found ,这表明即使模块由ModuleManager加载,这些模块的module.config.php配置也没有加载。

Code

In Module.php of my Application module I implemented InitProviderInterface and added a method init(ModuleManager $moduleManager) where I catch the moduleManager loadModules.post event trigger and load modules 在我的Application模块的Module.php中,我实现了InitProviderInterface并添加了一个方法init(ModuleManager $moduleManager) ,其中我捕获了moduleManager loadModules.post事件触发器并加载了模块

public function init(\Zend\ModuleManager\ModuleManagerInterface $moduleManager)
{
    $eventManager = $moduleManager->getEventManager();
    $eventManager->attach(\Zend\ModuleManager\ModuleEvent::EVENT_LOAD_MODULES_POST, [$this, 'onLoadModulesPost']);
}

Then in the same class I delcare the method onLoadModulesPost and start loading my dynamic modules 然后在同一个类中,我将方法onLoadModulesPost并开始加载我的动态模块

public function onLoadModulesPost(\Zend\ModuleManager\ModuleEvent $event)
{

    /* @var $serviceManager \Zend\ServiceManager\ServiceManager */
    $serviceManager = $event->getParam('ServiceManager');
    $configListener = $event->getConfigListener();

    $authentication = $serviceManager->get('zfcuser_auth_service');

    if ($authentication->getIdentity())
    {
        $moduleManager = $event->getTarget();

        ...
        ...

        $loadedModules = $moduleManager->getModules();
        $configListener = $event->getConfigListener();
        $configuration = $configListener->getMergedConfig(false);

        $modules = $modulesMapper->findAll(['is_agency' => 1, 'is_active' => 1]);

        foreach ($modules as $module)
        {
            if (!array_key_exists($module['module_name'], $loadedModules))
            {
                $loadedModule = $moduleManager->loadModule($module['module_name']);

                //Add modules to the modules array from ModuleManager.php
                $loadedModules[] = $module['module_name'];

                //Get the loaded module
                $module = $moduleManager->getModule($module['module_name']);

                //If module is loaded succesfully, merge the configs
                if (($loadedModule instanceof ConfigProviderInterface) || (is_callable([$loadedModule, 'getConfig'])))
                {
                    $moduleConfig = $module->getConfig();
                    $configuration = ArrayUtils::merge($configuration, $moduleConfig);
                }
            }
        }

        $moduleManager->setModules($loadedModules);
        $configListener->setMergedConfig($configuration);
        $event->setConfigListener($configListener);
    }
}

Questions 问题

  • Is it possible to achieve what I am trying ? 是否有可能实现我的目标?
  • If so, what is the best way ? 如果是这样,最好的方法是什么?
  • What am I missing in my code ? 我的代码中缺少什么?

I think there is some fundamental mistake in what you are trying to do here: you are trying to load modules based on merged configuration, and therefore creating a cyclic dependency between modules and merged configuration. 我认为你在这里尝试做的事情有一些根本性的错误:你正在尝试基于合并配置加载模块,因此在模块和合并配置之间创建循环依赖。

I would advise against this. 我会建议不要这样做。

Instead, if you have logic that defines which part of an application is to be loaded, put it in config/application.config.php , which is responsible for retrieving the list of modules. 相反,如果您有定义要加载应用程序的哪个部分的逻辑,请将其放在config/application.config.php ,该部门负责检索模块列表。

At this stage though, it is too early to depend on any service, as service definition depends on the merged configuration too. 但是在这个阶段,依赖任何服务还为时过早,因为服务定义也依赖于合并的配置。

Another thing to clarify is that you are trying to take these decisions depending on whether the authenticated user (request information, rather than environment information) matches a certain criteria, and then modifying the entire application based on that. 需要澄清的另一件事是,您正在尝试根据经过身份验证的用户(请求信息,而不是环境信息)是否与特定条件匹配,然后根据该修改整个应用程序来做出这些决策。

Don't do that: instead, move the decision into the component that is to be enabled/disabled conditionally, by putting a guard in front of it. 不要这样做:相反,通过在其前面放置一个警卫,将决定移动到有条件地启用/禁用的组件中。

What you're asking can be done, but that doesn't mean you should. 你要问的是什么,但这并不意味着你应该这样做。

Suggesting an appropriate solution without knowing the complexity of the application you're building is difficult. 在不知道您正在构建的应用程序的复杂性的情况下建议适当的解决方案很困难。

Using guards will certainly help decouple your code, however using it alone doesn't address scalability and maintainability, if that's a concern? 使用警卫肯定会帮助解耦你的代码,但是单独使用它并不能解决可扩展性和可维护性问题,如果这是一个问题呢?

I'd suggest using stateless token-based authentication. 我建议使用无状态基于令牌的身份验证。 Instead of maintaining the validation logic in every application, write the validation logic at one common place so that every request can make use of that logic irrespective of application. 不是在每个应用程序中维护验证逻辑,而是在一个公共位置编写验证逻辑,以便每个请求都可以使用该逻辑,而不管应用程序如何。 Choosing a reverse proxy server (Nginx) to maintain the validation logic (with the help of Lua) gives you the flexibility to develop your application in any language. 选择反向代理服务器(Nginx)来维护验证逻辑(在Lua的帮助下)使您可以灵活地以任何语言开发应用程序。

More to the point, validating the credentials at the load balancer level essentially eliminates the need for the session state, you can have many separate servers, running on multiple platforms and domains, reusing the same token for authenticating the user. 更重要的是,在负载均衡器级别验证凭据基本上消除了对会话状态的需要,您可以在多个平台和域上运行许多单独的服务器,重用相同的令牌来验证用户。

Identifying the user, account type and loading different modules then becomes a trivial task. 识别用户,帐户类型和加载不同的模块然后变成一项微不足道的任务。 By simply passing the token information via an environment variable, it can be read within your config/application.config.php file, without needing to access the database, cache or other services beforehand. 只需通过环境变量传递令牌信息,就可以在config/application.config.php文件中读取它,而无需事先访问数据库,缓存或其他服务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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