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]public function preDispatch(){
Zend_Layout::getMvcInstance()->setLayoutPath(‘../application/admin/layouts’);
}[/php]
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:
- Ability to register a layout for use with a specified module
- 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]
class Module_LayoutPlugin extends Zend_Controller_Plugin_Abstract {
/**
* Array of layout paths associating modules with layouts
*/
protected $_moduleLayouts;
/**
* Registers a module layout.
* This layout will be rendered when the specified module is called.
* If there is no layout registered for the current module, the default layout as specified
* in Zend_Layout will be rendered
*
* @param String $module The name of the module
* @param String $layoutPath The path to the layout
* @param String $layout The name of the layout to render
*/
public function registerModuleLayout($module, $layoutPath, $layout=null){
$this->_moduleLayouts[$module] = array(
‘layoutPath’ => $layoutPath,
‘layout’ => $layout
);
}
public function preDispatch(Zend_Controller_Request_Abstract $request){
if(isset($this->_moduleLayouts[$request->getModuleName()])){
$config = $this->_moduleLayouts[$request->getModuleName()];
$layout = Zend_Layout::getMvcInstance();
if($layout->getMvcEnabled()){
$layout->setLayoutPath($config['layoutPath']);
if($config['layout'] !== null){
$layout->setLayout($config['layout']);
}
}
}
}
}
[/php]
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]
//setup the layout
Zend_Layout::startMvc(array(
‘layoutPath’ => ‘../application/default/layouts’,
‘layout’ => ‘main’
));
$layoutModulePlugin = new Module_LayoutPlugin();
$layoutModulePlugin->registerModuleLayout(‘admin’,’../application/admin/layouts’);
$controller->registerPlugin($layoutModulePlugin);
[/php]
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.
#1 by Pavel on 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 by David on 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 by Antonio on 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.
#4 by Jason on November 15, 2008 - 7:15 pm
Thanks for showing me how to do this.
#5 by Andrew Chen on July 27, 2009 - 5:34 pm
hello! How do I get ahold of you? I’d like to talk more.
Here’s my bio: http://andrewchenblog.com/about/
Write me back at voodoo at gmail, please. Thanks.
#6 by Gites France on August 7, 2009 - 2:31 am
Thanks – it was easy to follow.
#7 by Deitrich Zook on September 15, 2009 - 8:08 pm
Thanks for showing the short simple way and the more extensive way using individual layouts for various modules. I am going to use the simple way for the time being as I only have one controller in the new module that needs a custom layout.
For anyone wanting an example based on this one, there is an example here:
http://www.zfforums.com/zend-framework-components-13/model-view-controller-mvc-21/disable-layout-entire-module-1723.html
#8 by EM on October 5, 2009 - 1:07 pm
Thanks – it was easy to follow.
#9 by DA on October 19, 2009 - 1:20 am
I did the following:
created a LayoutPlugin.php in the modules folder.
included that into the public/index.php
/setup the layout
Zend_Layout::startMvc(array(
‘layoutPath’ => APPLICATION_PATH . ‘modules/default/layouts/scripts’,
‘layout’ => ‘layout’
));
$layoutModulePlugin = new Module_LayoutPlugin();
$layoutModulePlugin->registerModuleLayout(’admin’,’../application/admin/layouts’);
$controller->registerPlugin($layoutModulePlugin);
and it keeps giving me an error
Fatal error: Class ‘Zend_Controller_Front’ not found in C:\wamp\www\project\public\index.php on line 20