Per Module Zend_Layout

Sometimes when you are building a web application, you want to use different layouts for different parts of the site. For example, in a content management system, you may want one layout for normal users and another, completely different layout for site administrators.

Assuming that you have the standard zend directory structure, you will have something like this:

/application
  /admin
    /controllers
    /layouts
    /views
  /default
    /controllers
    /layouts
    /views
  /feeds
    /controllers
    /views

Or something of the sort. When the user is in the admin section we want to use the layout folder from /admin, and when they are in the default section or in the feeds section of the site we would like to use the layout folder from /default.

We could simply go through all the controllers in the admin section and set the layout path in the init or preDisptach function:

PHP:
  1. public function preDispatch(){
  2.     Zend_Layout::getMvcInstance()->setLayoutPath('../application/admin/layouts');
  3. }

This may work well for this simple example. However, every time we wish to add a controller to the admin section it would be necessary to setup the layout path and if ever we want to change the layout path for the admin module, there would be a lot of search-replacing.

A Much better solution is to write a plugin for the Zend Front controller. This plugin extends the Zend_Controller_Plugin_Abstract type and will be run each time the front controller is invoked, before anything is rendered.

Our plugin must have some very basic functionality:

  1. Ability to register a layout for use with a specified module
  2. Ability to fall back to the default layout if there is no layout registered for the current module

The Layout Plugin is as follows:

PHP:
  1. class Module_LayoutPlugin extends Zend_Controller_Plugin_Abstract {
  2.    
  3.     /**
  4.      * Array of layout paths associating modules with layouts
  5.      */
  6.     protected $_moduleLayouts;
  7.    
  8.     /**
  9.      * Registers a module layout.
  10.      * This layout will be rendered when the specified module is called.
  11.      * If there is no layout registered for the current module, the default layout as specified
  12.      * in Zend_Layout will be rendered
  13.      *
  14.      * @param String $module        The name of the module
  15.      * @param String $layoutPath    The path to the layout
  16.      * @param String $layout        The name of the layout to render
  17.      */
  18.     public function registerModuleLayout($module, $layoutPath, $layout=null){
  19.         $this->_moduleLayouts[$module] = array(
  20.             'layoutPath' => $layoutPath,
  21.             'layout' => $layout
  22.         );
  23.     }
  24.    
  25.     public function preDispatch(Zend_Controller_Request_Abstract $request){
  26.         if(isset($this->_moduleLayouts[$request->getModuleName()])){
  27.             $config = $this->_moduleLayouts[$request->getModuleName()];
  28.            
  29.             $layout = Zend_Layout::getMvcInstance();
  30.             if($layout->getMvcEnabled()){
  31.                 $layout->setLayoutPath($config['layoutPath']);
  32.                
  33.                 if($config['layout'] !== null){
  34.                     $layout->setLayout($config['layout']);
  35.                 }
  36.             }
  37.         }
  38.     }
  39. }

The $_moduleLayouts property maintains the mapping between modules and layouts.

The registerModuleLayout function allows us to register a layoutpath and an (optional) layout name for a module.

The actual magic happens in the preDispatch function (which is called by the front controller before dispatching the request). The function checks to see if there is a layout set for the current module; if there is, it switches the layout path to use the module-specific path. Next it checks to see if there is a layout name associated with the layout registered for this module. If there is, it uses it, otherwise it falls back to the default layout name.

In our bootstrapper file (html/index.php) we must setup the default layout and module layout paths and then register the plugin with the front controller. The following code snippit is only a sliver of the bootstrapper and assumes that $controller is an instance of Zend_Controller_Front (eg: $controller = Zend_Controller_Front::getInstance();)

PHP:
  1. //setup the layout
  2. Zend_Layout::startMvc(array(
  3.     'layoutPath' => '../application/default/layouts',
  4.     'layout' => 'main'
  5. ));
  6.  
  7. $layoutModulePlugin = new Module_LayoutPlugin();
  8. $layoutModulePlugin->registerModuleLayout('admin','../application/admin/layouts');
  9.  
  10. $controller->registerPlugin($layoutModulePlugin);

It is important to setup the default layout so the plugin has a layout to fall back to if no module-specific layout is registered.

I hope that this tutorial has served as a quick and easy introduction to Zend Front Controller plugins and module-specific layouts.

5 Comments so far »

  1. Pavel said

    am August 27 2008 @ 2:02 pm

    Its very interesting your perspective. Personally I think Zend Framework development team should consider to include any official implementation about this issue, because this is a truly sharp way to work around it and solve it.

    Thanks for the tips, it was very helpful for me.

  2. David said

    am September 17 2008 @ 6:14 am

    This was very helpful to me. Although I don’t see the usefulness of registering a layout as the registration would need to take place before the plugin code.

    I have implemented this in a more dynamic way for my application. I have a Module Setup Plugin that will add module paths to the include path and then also checks to see if a module has a folder layouts with a main layout file with the same name of the module. If so, I use your code above to setLayoutPath and setLayout. It works nicely and is dynamic.

    I can see maybe taking this one step further and creating a module registration system where the module configuration is read and based on that setting up the module environment including layout.

    Thanks again,
    David

  3. Disable layout for entire module - Zend Framework Forum said

    am October 4 2008 @ 9:40 am

    [...] to find an answer himself… Two minutes after I posted the topic, I found an explanation here: Per Module Zend_Layout | Views From The Hill You can do it by calling PHP Code: $layout = Zend_Layout::getMvcInstance();  [...]

  4. Antonio said

    am November 10 2008 @ 9:03 am

    Hi. I am newbie to Zend Framework and i have a question about the layout management. In my application i have 3 modules accordingly the following directory structure.

    /application
    /admin
    /controllers
    /layouts
    /views
    /default
    /controllers
    /layouts
    /views
    /photogallery
    /controllers
    /views

    I use the “Module_LayoutPlugin” approch to switch between the default and the admin layouts.

    In the photogallery module there are different Controllers. Consider for example the CategoryController, a controller to manage the categories of the photogallery. In this controller there are the following actions: index, view, add, edit and delete. Now, when i am in the admin module and i want to add a category, i use this url http://mysite/photogallery/category/add but the application switch, obviously to the default layout while i want to remain in the admin layout.

    What’s the best strategy to fix it?

    Hello from Italy.

  5. Jason said

    am November 15 2008 @ 7:15 pm

    Thanks for showing me how to do this.

Comment RSS · TrackBack URI

Leave a comment

Name: (Required)

eMail: (Required)

Website:

Comment: