Change Layout in Controllers and Actions in Zend Framework 2

changing controller layouts in zend framework 2

In Zend Framework 2, if you want to change the layout just for one action or for every action in a controller, how do you do it? How do you do it without overriding the layout for every action throughout the entire application?

In today’s post, based on an excerpt from Zend Framework 2 for Beginners, we see how to achieve both of these requirements.

Zend Framework 2 implements the 2-Step View pattern, which allows for one set of view templates to handle logic such as headers, footers, sidebars and navigation blocks, logic not specific to any one action be kept separate from the view templates which render the content of a specific action.

If you’re creating your Zend Framework 2 projects using ZFTool, or basing them off of the ZF2 Skeleton App code, then you’ll know that the Application module contains a template alias in template_map for the default layout template file.

If not, here’s the definition:

'view_manager' => array(
    'template_map' => array(
        'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
    ),
),

When an application’s called, the view rendered by the dispatched action, will be rendered within module/Application/view/layout/layout.phtml and the combined result will be displayed, assuming you’ve not disabled layouts, which we’ll cover another time.

For the most part, that works fine. But what if you want to use different layouts per/module, per/controller, even per/action? Zend Framework 2 makes it easy to do this. In today’s post I’m going so show you three ways to do it, all varying in complexity.

1. Override the default layout in your module

The simplest is to override layout/layout in module.config.php in our module. So long as our module’s activated, after Application, then your module’s preferences will overwrite those in Application, making it the new default layout.

To do so, in your module’s module.config.php file, add or integrate the configuration above, specifying the path to a new layout file.

2. Override the layout per/action

But doing this replaces the layout globally, for every request. If we wanted to override the layout on an as-needed basis instead, we can call the controller’s layout() function, in an action, and pass a template name or alias to it.

Here’s an example of overriding the layout for the index action.

public function indexAction()
{
    $this->layout('layout/generic');
}

Assuming that we had a template alias, called ‘layout/generic’, defined the template_map for our module, then it would be rendered, instead of either the default or overridden layout template.

3. Override the layout per/controller

But if we call it in our action, the layout will only be overridden for that specific action. What if we want to override it for every action in the controller? There’s a couple of ways you can do this:

  1. You can add the call to layout in the Controller’s constructor
  2. You can define the setEventManager() function in the Controller, attaching to the dispatch event
  3. You can implement the init function in the module’s Module.php file, again attaching to the dispatch event and checking which controller is being requested

Overriding at the controller level

I’ll skip over number 1 and first implement the setEventManager() function in the Controller. In your controller add in the following use statement:

use Zend\EventManager\EventManagerInterface;

Then add in the following function.

public function setEventManager(EventManagerInterface $events)
{
    parent::setEventManager($events);
    $controller = $this;
    $events->attach('dispatch', function ($e) use ($controller) {
        $controller->layout('layout/generic');
    }, 100);
}

What we’ve done here, is to get access to the EventManager and attach a closure which will listen for the dispatch event, by specifying ‘dispatch’ as the first argument, a closure as the second and the event priority as the third.

The higher the priority the more important that event is. By passing the current controller object to the closure we can call the layout function on it, specifying the same template alias as before.

Now, when any action of this controller is dispatched, the closure will be executed, overriding the layout to the one we’ve specified. Actions in any other controller, in any other module, will use the default layout template as normal.

Overriding at the module level

Finally, let’s look at overriding the controller at the module level. Effectively the code we’ll create now is just the same as the last example, it’s just the location that’s different. In Module.php for your module, add the code below and let’s step through it.

use Zend\ModuleManager\ModuleManager;
public function init(ModuleManager $manager)
{
    $events = $manager->getEventManager();
    $sharedEvents = $events->getSharedManager();
    $sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
        $controller = $e->getTarget();
        if (get_class($controller) == 'Generic\Controller\IndexController')         {
            $controller->layout('layout/generic');
        }
    }, 100);
}

We place the code here because the init() function, if defined, is called on every page request. For more information on the available Module options, check out the manual.

What it does is to get access to the EventManager and attach a listener to the dispatch event as before, using a closure, with one small difference.

As we’re at the module, we have to add in a check to determine which controller’s handling the request. If it’s the right one, then the layout function is again called. If not, the original layout template is rendered.

And there you have it. Three simple ways to override layouts in your Zend Framework 2 modules. If you’re just starting off with Zend Framework 2, register today to know when Zend Framework 2 is published shortly.

Do you change layouts in your modules this way, or do you take a different approach? Share your approach in the comments.

Views