nstapelbroek nstapelbroek - 5 months ago 39
PHP Question

Change Zend Framework 2 default template resolver behavior

I've just started looking into Zend framework 2 .One thing that I can’t seem to figure out is how to change the behavior of the framework when its deciding what view template to use when i’m not passing it in the viewmodel.

When looking for the answer myself I found the following, which states that Zend resolves view templates using the pathing below:

{normalized-module-name}/{normalized-controller-name}/{normalized-action-name}


(Source: http://zend-framework-community.634137.n4.nabble.com/Question-regarding-template-path-stack-tp4660952p4660959.html)

Now I’m looking to edit or remove the normalized-module-name segment. All the view files stay in my module/views folder. The reason I want to change this is because I’m using sub namespaces as my module name, resulting in the first segment of the namespace as the normalized module name (which is not specific enough for me).

To give you an example, the module
Foo\Bar
will result in an example view being rendered from:

/modules/Foo/Bar/view/foo/test/index.phtml
.

I would like to change that default behavior to:

/modules/Foo/Bar/view/bar/test/index.phtml

Answer

I had a similar problem and here's my solution.

Default template injector is attached to an event manager of the current controller with priority -90, and it resolves a template name only if a view model is not provided with one. Knowing this, you can create your own template injector with a required logic and attach it to the event manager with the higher priority.

Please see the code below:

public function onBootstrap(EventInterface $event)
{
    $eventManager = $event->getApplication()->getEventManager();

    $eventManager->getSharedManager()
        ->attach(
            'Zend\Stdlib\DispatchableInterface',
            MvcEvent::EVENT_DISPATCH,
            new TemplateInjector(),
            -80 // you can put here any negative number higher -90
        );
}

Your template injector which resolves template paths instead of the default one.

class TemplateInjector 
{
    public function __invoke(MvcEvent $event)
    {
        $model = $event->getResult();

        if (!$model instanceof ViewModel)
        {
            return;
        }

        $controller = $event->getTarget();


        if ($model->getTemplate())
        {
            return ;
        }

        if (!is_object($controller))
        {
            return;
        }

        $namespace = explode('\\', ltrim(get_class($controller), '\\'));

        $controllerClass = array_pop($namespace);
        array_pop($namespace); //taking out the folder with controllers
        array_shift($namespace); //taking out the company namespace

        $moduleName = implode('/', $namespace);

        $controller = substr($controllerClass, 0, strlen($controllerClass) - strlen('Controller'));
        $action = $event->getRouteMatch()->getParam('action');
        $model->setTemplate(strtolower($moduleName.'/'.$controller.'/'.$action));
    }
}

Here's the link from my blog where I wrote about it in more details: http://blog.igorvorobiov.com/2014/10/18/creating-a-custom-template-injector-to-deal-with-sub-namespaces-in-zend-framework-2/