ZF3 Multiple Modules and Multiple Layouts

无人久伴 提交于 2019-12-02 04:12:43
rkeet

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!