Change Layout in Controllers and Actions in Zend Framework 2

If you want to change a layout for one or all actions in a controller, how do you do it without overriding the layout globally? Today’s post shows how.


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 a summary from Zend Framework 2 for Beginners, we see how to achieve both of these requirements.

Thanks to GeeH, over in #zftalk, for providing corrections to the earlier draft of this post.

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 define the setEventManager() function in the Controller, attaching to the dispatch event
  2. 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 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.

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


You might also be interested in these tutorials too...

Wed, May 31, 2017

Create Modules and Middleware with Command-Line Tooling Support

For the longest time, Zend Framework hasn’t had the strongest support for command-line tooling and scaffolding. However, in recent times, that’s all changed. Come find out how to use Expressive’s new tooling support to create modules and middleware rapidly.

Tue, Nov 15, 2011

Rename uploaded files with Zend Framework

Recently I was asked how to rename a file with the Zend Framework that used a Zend Form and Zend File element. They key requirement was that it should not be a hack or a kludged solution. So I thought I’d write a quick post to provide a simple example on how it was achieved.

Wed, Nov 9, 2011

The Zend Framework Bootstrap made simple (Part 3)

Ok, we’ve established that with the Zend Framework, we need to do a bit more work than some of the other frameworks to get up to speed - but that’s not necessarily a bad thing - right?! But it can be a bit tedious and it’s something as professional developers, we want to automate away. So we’ve been addressing in this series how to do just that with a custom, extendable bootstrap class.

In the first part of the series we laid the foundation of our custom bootstrap class by creating a custom class directory structure, adding its namespace to the application ini and modifying the default application bootstrap so that it extends from it and had a look at the first component - caching.

Then, in the second part of the series, we built on the foundation laid in part one by creating plugin resources for the routing table, application navigation and the database connections - these being some of the most common tasks, usually, associated with a web-based application.

In this, the third and final part of the series, I’m going to finish up by looking at application placeholders and surprise, no not logging as originally promised, but pagination. As an added extra, we’re going to be using a key feature of Zend Application to make it a breeze.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus