ZF3多个模块和多个布局 [英] ZF3 Multiple Modules and Multiple Layouts

查看:71
本文介绍了ZF3多个模块和多个布局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用Zend Framework 3的多模块站点上工作了大约6个月,并在不断学习中.在大多数情况下,我都取得了很大的成功,但遇到了以下将要描述的问题.有大约20个模块,我确定在每个模块module.config.php中,我都应该像这样为Layout定义一个唯一的名称.

  • 命名空间示例
  • 控制器名称:SampleController.php
  • module/Sample/view/sam_layout.phtml

并在示例模块module.config.php

'view_manager' => [
    'display_not_found_reason' => true,
    'display_exceptions'       => true,
    'doctype'                  => 'HTML5',
    'template_map' => [
    'layout/layout' => _DIR__ . '/../view/layout/sam_layout.phtml',
    'sample/sample/index' => __DIR__ . '/../view/sample/sample/index.phtml',
    ],
    'template_path_stack' => [
    'Sample' =>  __DIR__ . '/../view',

即使我记住命名空间,如果我将布局文件称为"layout.phtml",也不能确定预期的视图是否出现或仅部分正确.我注意到,如果我重新排列composer.json和modules.config.php中的模块名称,然后运行composer dump-autoload,无论是否给layout.phtml文件指定唯一的前缀,我都会得到一组不同的混合布局./p>

唯一可以清除此问题的方法是执行以下操作,例如清除composer.json和modules.config.php,除了Application模块,然后重新运行composer dump-autoload或更改模块名称,将其添加到composer.json& modules.config重新运行dump-autoload,然后放回模块名称..本质上是为了使似乎一直在使视图混乱的情况变硬,以松开配置并重新映射所有内容.请注意,当这个问题神奇地解决后,它就消失了,我们可以继续开发代码,好像没有什么错.

我应该注意,我们从开发模式下的框架应用程序开始.我实际上删除了data/cache文件夹中的所有文件.不同模块中的大多数视图共享导航栏,但是多个模块可以选择特定模块的导航栏视图,很难清除.

因此,如上所述,我有一些疑问.其他开发人员是否也有同样的问题,为什么我不能给布局脚本一个唯一的名称并期望该视图得到认可?随着我们的进一步发展,我确定每个模块都将具有更多的布局,并且sample/sample文件夹中当然还有更多的.phtml文件(从上面的示例开始),我们将继续看到这种现象吗? Apache Web Server或PHP7是否可以缓存导致此问题的文件和路径,而ZF3与它无关?注意:我没有启用opcache.

请保持仁慈:如果您将我埋在OO代码中,则可能不会帮助我了解正在发生的事情.

解决方案

如果您是ZF2/3的新手,这是一个合法的问题.

您应该知道的一件事是,所有配置(application.config.phpmodules.config.php以及每个模块module.config.php以及您定义的任何其他配置文件都应包含在Module.php类中),它们全部合并为一个单个配置数组.

这样,如果每个模块中都有以下位,那么使用array_merge_recursive合并时只有最后一个有效,而使用array_merge则只有第一个有效.

'view_manager' => [
    // ... others
    'template_map' => [
        'layout/layout' => _DIR__ . '/../view/layout/sam_layout.phtml',
        // ... others
    ],
],

因此,请确保:

  • 仅<一次>定义关联数组键!
  • 确保您出于某种原因覆盖了扩展模块中的值

我个人使用这两个规则.定义一次,然后在需要时覆盖.但是,要小心后者,很容易犯错误.

一个这样的错误是在Application模块中定义layout/layout,然后在User模块中再次定义. 永远不要这样做;)


首先解决您的配置问题

要给您一些清理过的配置,以免迷路,请使用AbstractModule.php类.我已经从某人的摘要中复制了我的照片(无法记住是谁,否则会记入您的名字).

namespace Your\Core\Or\Mvc\Module\Namespace;

use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;

/**
 * Class AbstractModule
 * @package Your\Core\Or\Mvc\Module\Namespace
 */
abstract class AbstractModule implements ConfigProviderInterface, AutoloaderProviderInterface
{
    /**
     * @var String Path of current module
     */
    protected $path;

    /**
     * @var String Namespace of current module
     */
    protected $namespace;

    /**
     * This is to be called by descendant classes with:
     * parent::__construct(__DIR__, __NAMESPACE__)
     *
     * @param $path      string Module path
     * @param $namespace string Module namespace
     */
    public function __construct($path, $namespace)
    {
        $this->path = $path;
        $this->namespace = $namespace;
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        $config = [];

        foreach (glob($this->path . '/config/*.php') as $filename) {
            $config = array_merge_recursive($config, include $filename);
        }

        return $config;
    }

    /**
     * @return array
     */
    public function getAutoloaderConfig()
    {
        return [
            'Zend\Loader\StandardAutoloader' => [
                'namespaces' => [
                    $this->namespace => $this->path . DIRECTORY_SEPARATOR . 'src',
                ],
            ],
        ];
    }
}

在每个模块中使用以下Module.php

namespace Your\Module\Namespace;

use Your\Core\Or\Mvc\Module\Namespace\AbstractModule;

/**
 * Class Module
 * @package Your\Module\Namespace
 */
class Module extends AbstractModule
{
    /**
     * Module constructor.
     */
    public function __construct()
    {
        parent::__construct(__DIR__, __NAMESPACE__);
    }
}

我为什么要使用所有这些面向对象的垃圾?

好吧,如果您有此代码,则可以按主题将模块中的所有配置文件分开.这样,您现在可以在APP_DIR/module/MODULE_NAME/config/文件夹中放置一堆配置文件,例如:

  • module.config.php
  • routes.config.php
  • custom.config.php

所有这些都将被加载.

注意:这不能解决您的布局被其他人覆盖的问题.这是因为所有配置都使用相同的配置键名称(layout/layout).


第二步配置以允许不同的布局

接下来,您希望每个模块使用不同的布局.没问题,但是,您需要确保允许进行设置的配置.因此,我们使用了一些代码片段.

如果您有专门用于主题的模块,请将此功能添加到其Module.php中.否则,您可能要将其添加到Application模块中的Module.php类中.

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

    /**
     * Source: https://github.com/Hounddog/HdRouteLayouts/blob/master/Module.php
     * Add below AND route_layouts => [ %route% => %template/layout% ] to a module to allow route based layout
     *
     * Below example applies layout in [layout/admin => [ %path/to/layout.phtml% ] to all routes starting with
     * "admin*" as defined in the "route_layouts => []" array.
     *
     *  'view_manager' => [
     *      'template_map' => [
     *          'layout/admin' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'view'
     *          . DIRECTORY_SEPARATOR . 'layout' . DIRECTORY_SEPARATOR . 'layout.phtml',
     *      ],
     *  ],
     *
     *  'route_layouts' => [
     *      'admin*' => 'layout/admin',
     *  ],
     */
    $e->getApplication()->getEventManager()->getSharedManager()
        ->attach(AbstractActionController::class, MvcEvent::EVENT_DISPATCH, function (MvcEvent $e) {
            $controller = $e->getTarget();
            $routeName = $e->getRouteMatch()->getMatchedRouteName();
            $config = $e->getApplication()->getServiceManager()->get('config');
            $layoutConfig = isset($config['route_layouts']) ? $config['route_layouts'] : [];

            if (isset($layoutConfig) && count($layoutConfig) > 0) {
                if (isset($layoutConfig[$routeName])) {
                    $controller->layout($layoutConfig[$routeName]);
                } else {
                    $rules = array_keys($layoutConfig);
                    foreach ($rules as $routeRule) {
                        if (fnmatch($routeRule, $routeName, FNM_CASEFOLD)) {
                            $controller->layout($layoutConfig[$routeRule]);
                            break;
                        }
                    }
                }
            }
        }, 100);
}

我们现在可以添加特定的路线布局!

我们通过在配置中注册新布局来添加它们.在我的一个项目中,我们添加了一些布局,这是添加的配置,以基于路线使用不同的布局.

// 'route_layouts' is a new "top-level" config array key
// Here you define: route -> template_name
'route_layouts' => [
    '*'           => 'layout/layout', 
    'login'       => 'layout/login',
    'register'    => 'layout/login',
    'error*'      => 'error/error',
    'error/404'   => 'error/404',
    'error/index' => 'error/index',
],
'view_manager' => [
    'template_map' => [
        // Here you define: template_name -> location
        'layout/layout'   => __DIR__ . '/../view/layout/layout.phtml',
        'layout/login'    => __DIR__ . '/../view/layout/login.phtml',
        'layout/register' => __DIR__ . '/../view/layout/register.phtml',
        'error/error'     => __DIR__ . '/../view/error/error.phtml',
        'error/404'       => __DIR__ . '/../view/error/404.phtml',
        'error/index'     => __DIR__ . '/../view/error/index.phtml',
    ],
    // ... other config
],


希望这可以为您解决很多有关配置和布局的问题.如果您无法在这些示例中使用相关问题,请随意发表评论.

I'm working on a multi module site using Zend Framework 3 for ~6 months, learning as I go along. For the most part I have been pretty successful but I have run across a problem that I will describe below. There are ~20 modules and I have determined that in each modules module.config.php I should define a unique name for the Layout like this.

  • Namespace Sample
  • Controller Name: SampleController.php
  • module/Sample/view/sam_layout.phtml

And in the Sample modules module.config.php

'view_manager' => [
    'display_not_found_reason' => true,
    'display_exceptions'       => true,
    'doctype'                  => 'HTML5',
    'template_map' => [
    'layout/layout' => _DIR__ . '/../view/layout/sam_layout.phtml',
    'sample/sample/index' => __DIR__ . '/../view/sample/sample/index.phtml',
    ],
    'template_path_stack' => [
    'Sample' =>  __DIR__ . '/../view',

If I call the layout file 'layout.phtml' even though I'm mindful of Namespaces it is not certain that the intended view appears or is only partially correct. I noticed if I rearrange the module names in composer.json and modules.config.php then run composer dump-autoload I get a different set of mixed up layouts regardless of if I give the layout.phtml file a unique prefix or not.

The only thing that appears to clear this up is to do something like clear out the composer.json and modules.config.php except for the Application module then re-run composer dump-autoload or change the modules name add it to the composer.json & modules.config re-run dump-autoload and then put the module name back.. Essentially shaking up the config in order to force what ever seems to be keeping the views scrambled to let go and re-map everything. Note that when this problem magically clears up it is gone and we can continue developing code as if nothing was ever wrong.

I should note we start with the skeleton-application in development mode. I actually delete any files in the data/cache folder. Most of the views in the different modules share the Nav Bar but multiple modules could pick a particular modules Nav Bar view and it is very difficult to clear out.

So having described the above I have some questions. Do other developers have this same problem why can't I give a layout script a unique name and expect the view to be honored? As we develop further I'm sure each module will have more layouts and of course more .phtml files in the sample/sample folder (Taking the example from above) will we continue to see this behavior? Could the Apache Web Server or PHP7 be caching files and paths causing this problem and ZF3 has nothing to do with it? Note: I don't have opcache enabled.

Please be merciful: if you bury me in OO code it likely won't help me understand what is happening.

解决方案

This is legitimate question if you're new to ZF2/3.

One thing you should know is that all configuration (application.config.php, modules.config.php and every modules module.config.php and any other config file you define should be included e.g. in Module.php class), they all get merged into a single config array.

As such, if you have the following bit in every one of your modules, only the last one will be valid if you merge using array_merge_recursive and only the first if you use array_merge.

'view_manager' => [
    // ... others
    'template_map' => [
        'layout/layout' => _DIR__ . '/../view/layout/sam_layout.phtml',
        // ... others
    ],
],

As such, make sure to either:

  • Define associative array keys only once!
  • Make sure you're overwriting the value in an extending module for a reason!

Personally, I use both of these rules. Define something once and overwrite when required. However, be careful with the latter, it's very easy to make a mistake.

One such mistake is to define layout/layout in your Application module and define it again in your User module and again in another. Don't do that, ever ;)


First fix your config issue

To give you a bit of a cleaned up config, so that you don't lose your way, use an AbstractModule.php class. I've copied mine from someone's snippet (cannot recall who from, otherwise would credit).

namespace Your\Core\Or\Mvc\Module\Namespace;

use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;

/**
 * Class AbstractModule
 * @package Your\Core\Or\Mvc\Module\Namespace
 */
abstract class AbstractModule implements ConfigProviderInterface, AutoloaderProviderInterface
{
    /**
     * @var String Path of current module
     */
    protected $path;

    /**
     * @var String Namespace of current module
     */
    protected $namespace;

    /**
     * This is to be called by descendant classes with:
     * parent::__construct(__DIR__, __NAMESPACE__)
     *
     * @param $path      string Module path
     * @param $namespace string Module namespace
     */
    public function __construct($path, $namespace)
    {
        $this->path = $path;
        $this->namespace = $namespace;
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        $config = [];

        foreach (glob($this->path . '/config/*.php') as $filename) {
            $config = array_merge_recursive($config, include $filename);
        }

        return $config;
    }

    /**
     * @return array
     */
    public function getAutoloaderConfig()
    {
        return [
            'Zend\Loader\StandardAutoloader' => [
                'namespaces' => [
                    $this->namespace => $this->path . DIRECTORY_SEPARATOR . 'src',
                ],
            ],
        ];
    }
}

Use in each of your modules the following Module.php

namespace Your\Module\Namespace;

use Your\Core\Or\Mvc\Module\Namespace\AbstractModule;

/**
 * Class Module
 * @package Your\Module\Namespace
 */
class Module extends AbstractModule
{
    /**
     * Module constructor.
     */
    public function __construct()
    {
        parent::__construct(__DIR__, __NAMESPACE__);
    }
}

Why would I use all of this OO muck?

Well, if you have this code, you can separate all of your config files, in your modules, by subject. As such, in a APP_DIR/module/MODULE_NAME/config/ folder you could now put a bunch of config files, such as:

  • module.config.php
  • routes.config.php
  • custom.config.php

And all of them would be loaded.

Mind: this does not address the issue you had of layouts being overwritten by others. This is due to using the same config key name (layout/layout) for all of your config.


Second set up config to allow for different layouts

Next, you want to use different layouts per module. This is no problem, however, you need to make sure you allow for configuration that sets it. As such, we use a little snippet of code.

If you have a module specifically for your Themes, add this function to its Module.php. Else, you might want to add this to the Module.php class in the Application module.

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

    /**
     * Source: https://github.com/Hounddog/HdRouteLayouts/blob/master/Module.php
     * Add below AND route_layouts => [ %route% => %template/layout% ] to a module to allow route based layout
     *
     * Below example applies layout in [layout/admin => [ %path/to/layout.phtml% ] to all routes starting with
     * "admin*" as defined in the "route_layouts => []" array.
     *
     *  'view_manager' => [
     *      'template_map' => [
     *          'layout/admin' => __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'view'
     *          . DIRECTORY_SEPARATOR . 'layout' . DIRECTORY_SEPARATOR . 'layout.phtml',
     *      ],
     *  ],
     *
     *  'route_layouts' => [
     *      'admin*' => 'layout/admin',
     *  ],
     */
    $e->getApplication()->getEventManager()->getSharedManager()
        ->attach(AbstractActionController::class, MvcEvent::EVENT_DISPATCH, function (MvcEvent $e) {
            $controller = $e->getTarget();
            $routeName = $e->getRouteMatch()->getMatchedRouteName();
            $config = $e->getApplication()->getServiceManager()->get('config');
            $layoutConfig = isset($config['route_layouts']) ? $config['route_layouts'] : [];

            if (isset($layoutConfig) && count($layoutConfig) > 0) {
                if (isset($layoutConfig[$routeName])) {
                    $controller->layout($layoutConfig[$routeName]);
                } else {
                    $rules = array_keys($layoutConfig);
                    foreach ($rules as $routeRule) {
                        if (fnmatch($routeRule, $routeName, FNM_CASEFOLD)) {
                            $controller->layout($layoutConfig[$routeRule]);
                            break;
                        }
                    }
                }
            }
        }, 100);
}

We can now add specific route layouts!

We add new layouts by registering them in the configuration. From one of my projects, we have a few layouts added, here's the config added to use the different layouts, based on routes.

// 'route_layouts' is a new "top-level" config array key
// Here you define: route -> template_name
'route_layouts' => [
    '*'           => 'layout/layout', 
    'login'       => 'layout/login',
    'register'    => 'layout/login',
    'error*'      => 'error/error',
    'error/404'   => 'error/404',
    'error/index' => 'error/index',
],
'view_manager' => [
    'template_map' => [
        // Here you define: template_name -> location
        'layout/layout'   => __DIR__ . '/../view/layout/layout.phtml',
        'layout/login'    => __DIR__ . '/../view/layout/login.phtml',
        'layout/register' => __DIR__ . '/../view/layout/register.phtml',
        'error/error'     => __DIR__ . '/../view/error/error.phtml',
        'error/404'       => __DIR__ . '/../view/error/404.phtml',
        'error/index'     => __DIR__ . '/../view/error/index.phtml',
    ],
    // ... other config
],


Hopefully this clears up a lot for you concerning your issues with configuration and layouts. Feel free to comment with a related question if you cannot get it working with these examples.

这篇关于ZF3多个模块和多个布局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆