Tag Archives: Zend Framework 2

Screenshot

Events Reference for ZF2 Applications

One of the most powerful aspects of a Zend Framework 2 application is the event manager. Right out of the box a skeleton application has impressive features which can be added to or replaced using events. Events also make it possible to write modules that can interact with the application while keeping its own code completely separate.

The downside to events is that they add a layer of abstraction that makes it more difficult to understand and trouble shoot an application. To write new event listeners or trouble shoot existing ones you need to understand how they all work together, but it is difficult to even know what listeners are attached to which events. As your application becomes more complicated it becomes more difficult to keep track of how all the pieces fit together.

To help myself keep track of event listeners in my applications I wrote a view helper that can inspect an Event Manager object and render a list of events and their corresponding listeners. You can see it in action with this live demo of events reference. This alone is helpful to understand the basic structure of a ZF2 application, but to take full advantage of the helper you’ll want to be able to use it in your own application. Lucky for you I added it to the Spork Tools module and you find the source code on GitHub.

To use the view helper all you need to do is pass an event manager object to it as a parameter. Keep in mind this could reveal security sensitive information so you should make sure this is not publicly accessable.

// controller action
public function indexAction()
{
    return array(
        'events' => $this->getApplication()->getEventManager(),
    );
}

// view script
echo $this->sporkToolsEvents($events);

Integrating Stylus, Less or Sass in a Zend Framework 2 application

I’ve been using CSS preprocessor languages for a while. I’ve tried Less, Sass and have adopted Stylus for most of my projects. All three make creating, maintaining and minimizing complicated style sheets easier, but come at the price of having to recompile after every update. Part of the appeal of script languages like PHP, JavaScript, HTML and CSS is that you don’t have to recompile to test changes, which speeds up development. The power of using a CSS preprocessor easily outweighs the inconvenience of compiling the code, but it still left me looking for a better way to integrate them into my workflow.

So I decided to write a PHP wrapper class for CSS preprocessors to integrate them into my Zend Framework 2 applications. This lets me trigger a CSS update in a controller or with a listener that updates the CSS when it is out of date. It also lets me use CSS preprocessors to include CSS inline in view templates, but I’ll go into that in another post. The result is I can edit Less, Sass or Stylus files and the changed CSS immediately shows up in my development environment without having to go back to the command line and all it takes is a minimal amount of configuration in my application.

I included the classes in the Spork ZF2 Library. The source code for the classes is available on GitHub here and there is some documentation in README.md.

Once you have the preprocessor installed and the classes are available in your application all you need to do is configure your application to use the Update Listener, tell it which preprocessor to use, where your source files are and where you want the CSS to go. Here is the basic configuration to use Stylus.

    'css-update' => array(
        'compiler' => 'cssStylus',
        'builds' => array(
            array(
                'source' => 'path/to/source',
                'destination' => 'path/to/destination',
                'includes' => array('path/to/include') // optional
                'compress' => true // optional
            ),
        ),
    ),
    'service_manager' => array(
        'invokables' => array(
            'cssStylus' => 'Spork\CSS\Stylus',
            'cssUpdateListener' => 'Spork\CSS\UpdateListener', 
        ),
    ),
    'listeners' => array(
        'cssUpdateListener',
    ),

A couple notes. I do not recommend using this in a production environment. I use this in my development environment and uploaded the CSS files to my production environment. The destination folder must be writable by the webserver.

Disable Layout in Zend Framework 2

It never fails. Every time I start a new ZF2 project I end up needing to disable the layout for certain responses. Usually it’s to feed some HTML content to a Dojo Dialog or something else involving XHR. Being a firm believer in RTFM first I look through the ZF2 Reference Guide which provides no help. Then I dive into the ZF2 source code which also provides no answers. After a bit of cursing my frustration leads me to Google which yields Zend Framework 2 : Disable Layout in specific Module. OK that was more difficult than is should have been, but at least it works… except… the obsessive compulsive part of me just can’t let it go without knowing “Why does it work?”, “Why isn’t there a simpler solution?” So to hopefully save myself, and other obsessive compulsive programmers, from future frustration here is the how and why.

Looking at the source code the MVC seems to create the layout out of some kind of magical programming aether. The magic starts way back during the Application’s bootstrap event when the ViewManager object is created. As part of it’s bootstrap listener the ViewManager initializes the MvcEvent’s ViewModel and sets its template to the configured layout. So how does it get the content into the layout? This happens during the dispatch event in the InjectViewModelListener which sets the ViewModel result returned from the dispatched controller as the child of the ViewModel created by the ViewManager. The trick of disabling the layout works because of 4 very inconspicuous lines in the InjectViewModelListener which replace the default ViewModel (which renders the layout) with the ViewModel returned from the controller action, if it’s terminal flag is true, instead of setting it as a child.

OK lets back up and try something simple to illustrate. We can disable the layout in a controller action by simply returning a ViewModel with the terminal flag set to true.

class MyController extends AbstractActionController
{
    public function myAction()
    {
        $viewModel = new \Zend\View\Model\ViewModel();
        $viewModel->setTerminal(true);
        return $viewModel;
    }
}

Pretty strait forward. When the InjectViewModelListener checks the terminal flag of our ViewModel it will set it as the ViewModel for MvcEvent, which replaces the default ViewModel and disable the layout. So how about disabling the layout for the entire controller?

class MyController extends AbstractActionController
{
    public function myAction()
    {
        $myViewModel = new \Zend\View\Model\ViewModel();
        return $myViewModel;
    }
    protected function attachDefaultListeners()
    {
        parent::attachDefaultListeners();
        $eventManager = $this->getEventManager();
        $eventManager->attach(
            \Zend\Mvc\MvcEvent::EVENT_DISPATCH,
            function(\Zend\Mvc\MvcEvent $event) {
                $myViewModel = $event->getResult();
                if ($myViewModel instanceof \Zend\View\Model\ViewModel) {
                    $myViewModel->setTerminal(true);
                }
            },
            -99);
    }
}

Still pretty strait forward. This time we attach a callback to the dispatch event which sets the terminal flag to true for any ViewModel returned from any action in controller. Notice the priority of -99 which places the listener just before the InjectViewModelListener but after any other listeners which modify the results of the controller’s action. This is especially important if any of the controllers actions return something other than a ViewModel such as an array or null.

So to disable the layout for all XMLHttpRequests for the entire application we should be able to use the same callback with a condition that tests for a XMLHttpRequest in our Module class… right? Well not quite. The problem is that the InjectViewModelListener doesn’t listen for the Application’s dispatch event; it is attached to the Controller’s dispatch event. The fact that the dispatch event triggers another dispatch event makes me want to throw my hands up, call it a day and head to the pub, but I’ll press on a little more. To make it work outside of the controller we need to use the shared event manager and attach it to the dispatch event for any controller.

namespace MyModule;

class Module
{
    public function onBootstrap(\Zend\Mvc\MvcEvent $event)
    {
        $sharedEventManager = $event->getApplication()
            ->getEventManager()->getSharedManager();
        $sharedEventManager->attach(
                'Zend\Mvc\Controller\AbstractController',
                \Zend\Mvc\MvcEvent::EVENT_DISPATCH,
                function(\Zend\Mvc\MvcEvent $event) {
                    $request = $event->getRequest();
                    if ($request->isXMLHttpRequest()) {
                        $dispatchResult = $event->getResult();
                        if ($dispatchResult instanceof ViewModel) {
                            $dispatchResult->setTerminal(true);
                        }
                    }
                },
                -99);
    }
}

This does the same thing, but uses the shared event manager to attach the listener to all controllers. As confusing as having the dispatch event trigger another dispatch event is it is also what allows us to target specific modules or even controllers. In the code above ‘Zend\Mvc\Controller\AbstractController’ could be replaced with a specific module or controller.

To make things a little more portable you could also add the following class to your library.

<?php
namespace MyLib\Mvc\Listener;

use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\MvcEvent;
use Zend\View\Model\ViewModel; 

class XMLRequestListener extends AbstractListenerAggregate
{
    public function attach(EventManagerInterface $events)
    {
        $sharedEvents = $events->getSharedManager();
        $this->listeners[] = $sharedEvents->attach(
            'Zend\Mvc\Controller\AbstractController',
            MvcEvent::EVENT_DISPATCH,
            array($this, 'handleXMLRequest'),
            -99);
    }

    public function handleXMLRequest(MvcEvent $event)
    {
        $request = $event->getRequest();
        if ($request->isXMLHttpRequest()) {
            $dispatchResult = $event->getResult();
            if ($dispatchResult instanceof ViewModel) {
                $dispatchResult->setTerminal(true);
            }
        }
    }
}

Then all you would need to do is add the following to your module

public function onBootstrap(MvcEvent $event)
{
    $eventManager = $event->getApplication()->getEventManager();
    $eventManager->attachAggregate(
            new MyLib\Mvc\Listener\XMLRequestListener());
}