【问题标题】:ZF3 Multiple Modules and Multiple LayoutsZF3 多模块多布局
【发布时间】:2018-06-21 09:40:30
【问题描述】:

我在一个使用 Zend Framework 3 的多模块站点上工作了大约 6 个月,边学习边学习。在大多数情况下,我都非常成功,但我遇到了一个问题,我将在下面描述。大约有 20 个模块,我已经确定在每个模块 module.config.php 中我应该像这样为布局定义一个唯一的名称。

  • 命名空间示例
  • 控制器名称:SampleController.php
  • 模块/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 文件一个唯一的前缀。

似乎可以清除此问题的唯一方法是清除除 Application 模块之外的 composer.json 和 modules.config.php 然后重新运行 composer dump-autoload 或更改模块名称将其添加到composer.json & modules.config 重新运行dump-autoload,然后放回模块名称。本质上是动摇配置,以迫使似乎一直保持视图混乱的东西放手并重新映射所有内容。请注意,当这个问题神奇地消失时,它就消失了,我们可以继续开发代码,就好像什么都没出错一样。

我应该注意我们从开发模式下的骨架应用程序开始。我实际上删除了数据/缓存文件夹中的所有文件。不同模块中的大多数视图共享 Nav Bar,但多个模块可以选择特定模块的 Nav Bar 视图,并且很难清除。

所以在上面描述了我有一些问题。其他开发人员是否有同样的问题,为什么我不能给布局脚本一个唯一的名称并期望视图得到尊重?随着我们进一步发展,我确信每个模块都会有更多的布局,当然还有更多的 .phtml 文件在 sample/sample 文件夹中(以上面的例子为例)我们会继续看到这种行为吗? Apache Web Server 或 PHP7 是否正在缓存导致此问题的文件和路径,而 ZF3 与此无关?注意:我没有启用 opcache。

请见谅:如果你把我埋在 OO 代码中,它可能无法帮助我理解正在发生的事情。

【问题讨论】:

  • 刚刚根据您的问题想到了两件事(即使在下面回答)。 1)I have determined that in each modules [...] I should define a unique name for the Layout。这不是真的,除非您的应用程序需要这样做。通常你会有 1 或 2 个布局(私人/管理员和公共)。基于应用程序,当然可以更多。 2) 在你的配置中只定义一次,因为一切(所有模块)都变成 1 个数组,除非你非常确定要从供应商模块中覆盖某些东西,例如在您自己的用户模块中用于 ZfcUser 的实体。否则,只声明一次!
  • 在不同的模块中为了相同的目的使用相同的配置只会让你很头疼。例如。这个位:'view_manager' => [ 'display_not_found_reason' => true, -> 不需要把它放在所有模块中。实际上,如果您希望它是 false 并且您必须将所有 20 个模块更改为 false,那会很烦人。因此,只需在您的应用程序模块的application.config.phpmodule.config.php 中添加它,但不能同时包含,也绝对不是全部。

标签: zend-framework3


【解决方案1】:

如果您是 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 类。我从某人的 sn-p 中复制了我的(不记得是谁的,否则会记入)。

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__);
    }
}

我为什么要使用所有这些 OO 垃圾?

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

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

所有这些都会被加载。

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


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

接下来,您想为每个模块使用不同的布局。这没问题,但是,您需要确保允许设置它的配置。因此,我们使用了一些 sn-p 代码。

如果您有专门用于主题的模块,请将此功能添加到其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
],

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

【讨论】:

  • 非常有用的回复!! +100 ;) 由于 config 文件夹与 src 文件夹在同一级别,我不得不写 ' public function construct() { parent::__construct(__DIR . '/..', 命名空间); }'
猜你喜欢
  • 2013-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多