FALVEY MEMORIAL LIBRARY



You are exploring: VU > Library > Blogs > Library Technology Development > VuFind, Zend Framework 2, and Flash Messages

VuFind, Zend Framework 2, and Flash Messages

One minor weakness of VuFind 1.x is that it doesn’t always provide good feedback for user actions.  One of the advantages to switching to a standard framework is that we can leverage an existing “flash message” mechanism to display messages to users in a consistent way.  Zend Framework 2 includes a simple FlashMessenger helper, but it took a bit of learning to use it as effectively as possible.  I hope that a discussion of my solution to this problem will helpfully demonstrate some aspects of the framework and VuFind 2.x’s architecture.

The Problem

In Zend Framework, flash messages are stored in an object by the controller.  For example:

$this->flashMessenger()->setNamespace('info')
    ->addMessage('Email sent successfully.');

or

$this->flashMessenger()->setNamespace('error')
    ->addMessage('Operation failed.');

 

The view is then responsible for pulling information out of the object and formatting it for display.

I wanted VuFind to display messages in a consistent way, so it seemed to make the most sense to use a View Helper to render flash messages.  This helper could take care of pulling messages from the namespaces in the object, applying translation, and building <div> tags with appropriate classes. This way, all I would have to do is add this code to a template:

<?=$this->flashMessages()?>

 

and my flash messages would display.

There’s just one hard part: getting the object containing the messages from the controller to the view with a minimum of fuss.

Solution #1: Brute Force

My first solution was not hard to come up with, but I was never happy with it.

Controllers in Zend Framework 2 pass values to their corresponding views by returning a value (often a \Zend\View\Model\ViewModel object).  If we want to ensure that all of these objects contain a particular value, we can establish a convention where VuFind’s controllers never construct a ViewModel directly; instead, they always call a $this->createViewModel() method defined in an abstract base class.  This helper method is responsible for constructing a ViewModel and then attaching the flash messenger object to it.  Once the ViewModel has a flash messenger attached, the View Helper responsible for rendering the messages can simply pull the messenger from a known named property in the model.

Advantage: It works.

Disadvantage: It’s ugly.  It relies on a particular arbitrary value being in a particular arbitrary place.  It takes away some of the flexibility of the framework by forcing the user to always return ViewModels, and to construct them in a particular way.  It makes the user jump through a hoop (calling createViewModel to create a view model) and punishes them in an unintuitive way if they don’t comply (flash messages stop appearing).

Solution #2: Working with the Framework

Fortunately, there is a better way.

One of the nice features of ZF2 is its service manager system (described in more detail in this blog post).  Service managers are responsible for constructing and storing instances of important objects used by the application.  Ugly application-specific details can be pushed into service manager configuration so that the actual components of the application can be simpler and more reusable.

The flash messenger controller helper and our custom view helper are both constructed by service managers.  This means that, through proper service manager configuration, we can ensure that the flash messages view helper always has access to the flash messenger controller helper, with no need for the intervention of a nasty createViewModel() method.

This is achieved with the help of a factory in our view helper service manager configuration:

'flashmessages' => function ($sm) {
    $messenger = $sm->getServiceLocator()
        ->get('ControllerPluginManager')
        ->get('FlashMessenger');
    return new \VuFind\View\Helper\Root\Flashmessages(
        $messenger
    );
},

 

Here’s how it works: service manager factories are passed an instance of the service manager that is doing the object construction ($sm in the example above — an instance of the service manager devoted to view helpers). $sm->getServiceLocator() retrieves the service manager that constructed $sm — the top-level service manager that manages the whole VuFind application. From here, we can call ->get(‘ControllerPluginManager’) to obtain the service manager for controller plugins. We can then call ->get(‘FlashMessenger’) on the controller plugin service manager to get hold of the flash messenger. This object ($messenger) is then passed to the constructor of the new \VuFind\View\Helper\Root\Flashmessages helper. The object now has access to the flash messenger as soon as it is constructed. None of the other code needs to know about the details — it just works like magic.

Yes, this is a bit ugly. But the important thing is that the ugliness is now in the configuration of the application — part of the glue that holds everything together. It’s okay for this part of the code to be a bit convoluted and application-specific, because its whole purpose is to make the various parts of the application fit together. The beautiful thing is that those component parts don’t have to know anything about the details of how the glue works — as a whole, the application has become less coupled and more flexible. Compare this to solution #1, where the ugliness was more deeply embedded in the application’s controllers and in the behavior of the view helper itself.

The Code

If you want to see how VuFind transitioned from Solution #1 to Solution #2, take a look at these diffs.

The Bottom Line

There’s definitely a learning curve involved with ZF2, but I continue to feel that it is worth the effort.  I hope this example has shed a little light on some of the issues… but if anything is unclear (and I’m sure something is), feel free to ask questions.  I’m always happy to help, especially if it means that more people are potentially able to help with the process of improving the code.

Like

0 Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

*

 


Last Modified: January 24, 2013