Themes in Zend Framework 2

Creating visual themes in your Zend Framework 2 (ZF2) application and switching between them is easier than you think.

I am often asked the following question:

How to create a completely separate mobile theme for our application? Not just using a new layout but also being able to override separate view templates, if needed.

I will extend this question to a more broader one:

How to create different themes in ZF2? Is that possible at all?

The short answer is: yes it is possible. In the next paragraphs I will show you one possible solution. Bear in mind that I will use some terms from the ZF2 world and you may need a bit of support from books like “Learn ZF2″ or the online manual to understand the explanations better. It might be a good idea to read also the previous article about View Models and Rendering.

The solution below will be created in a separate module called “Theme”. But you can quite happily make it part of your Application module.

First in out Module.php we need to attach an event listener that is executed before the actual rendering is happening. This can be done using the following code:

namespace Theme;

use Zend\Mvc\MvcEvent;


class Module
{
    public function onBootstrap(MvcEvent $event)
    {
        $eventManager = $event->getApplication()->getEventManager();
        $eventManager->attach(MvcEvent::EVENT_RENDER,array($this,'prepareTheme'),100);
    }
    //...
}

The import line above is that one:

$eventManager->attach(MvcEvent::EVENT_RENDER,array($this,'prepareTheme'),100);

It says attach the method prepareTheme from the current class to be executed with priority 100. 100 is higher than 1, which is the default priority and that guarantees us that prepareTheme will be executed before the actual rendering.

Before we jump to our prepareTheme method let’s see how we can define a “theme” and specify the view templates that have to be different in this theme. One possible solution is to use syntax which is similar to defining template_map and template_path_stacks in a view_manager section.

module//module.config.php

return array(
 // ...
 'themes' => array (
        'mobile' => array(
            'description' => 'Application Mobile Theme',
            'screenshot' => 'http://learnzf2.com/wp-content/uploads/2013/10/logo.png'
            'template_map' => array (
                'application/index/index' => __DIR__ .'/../theme/mobile/application/index/index.phtml',
                'layout/layout' => __DIR__ .'/../theme/mobile/layout.phtml',
            ),
            'template_path_stack' => array(
                __DIR__ . '/../theme/mobile/',
            ),
        )
    )

  //...
);

What we have above is a key themes in the configuration. Under that key there is another key with the unique name of the theme that we describe. In the case above this is mobile. And the array inside describes what we override. We used the same syntax for defining view templates as in the view_manager section and we added two more fields description and screenshot. The first has the short description for the theme and the second, which can be optional, points to the location from where our theme screenshot can be viewed.

In the prepareTheme method what we will do is check if there is a theme that is set and if yes we will try to instruct the ZF2 resolver to use the templates for that theme.

    
    public function prepareTheme(MvcEvent $event)
    {
        $services = $event->getApplication()->getServiceManager();
        $themes = $services->get('theme');
        $config = $services->get('config');

        // From our themes service we get the current theme that is set, if any
        $themeName = $themes->getName();
        if (!$themeName) {
               return;
        }

        $themeConfig = $themes->getConfig();
        $themeConfig = $themeConfig[$themeName];

        // !! Here Comes The Magic (R) !!

        // We override the template resolver
        // Here we add the changes that need to be applied to the existing template map
        if (isset($themeConfig['template_map'])) {
            $map = $services->get('ViewTemplateMapResolver');
            $map->merge($themeConfig['template_map']);
        }

        //  And we put our theme paths on top of the stack.
        //     This way if there is template in our theme it will be taken and used
        //     Otherwise we will use the ones provided earlier from the application
        if (isset($themeConfig['template_path_stack'])) {
            $stack = $services->get('ViewTemplatePathStack');
            $stack->addPaths($themeConfig['template_path_stack']);
        }
    }

And the above code is where most of the magic happens. Using that approach we can have themes that override the layout or selected templates and do not require us to override all existing application templates.

You can check the complete source code from here: https://github.com/slaff/learnzf2-theme. It contains all the tiny details needed to be able to switch between different themes and to decide which theme is the current one. If you use composer you can require the learnzf2/theme package.

A sample theme using that module can be found here (github, packagist: learnzf2/example-theme ).
Disclaimer: The sample theme is there to demonstrate the configuration and file structure needed to create a theme. It is very far from being good visual example. And we would be very happy if someone with good designer skills is willing to change that.

Enjoy 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

Captcha check * Time limit is exhausted. Please reload CAPTCHA.