How to replace the “Action” helper in ZF 2 (and make great widgetized content)

Pour la version française de cet article, c’est ici !
For the Russian version, click here (thanks Oleg Lobach) ! 

I had a great discussion yesterday with Robert Basic about the missing “Action” view helper in Zend Framework 2. As many people ask about it, I decided to blog about it. I was asking him about it because ZfcTwig (the official ZF 2 module to Twig, an awesome template engine by Fabien Potencier, try it out, it makes your views sexy) has a built-in “Action” helper, but I had the feeling something was wrong about that.

How it worked in ZF 1

Back in ZF 1, the action helper was mainly used for “widgetized” content, and allowed to start a new dispatch process to another action within the view. It worked like this :

This code just calls the “list” action of the “CommentController” with some parameters, and returns the HTML code from it. Nice and easy, isn’t it ?

However, this suffers from a lot of flaws. The first of them is performance. Because this begins a new MVC process is began (dispatch, routing…). Of course, all of the hooks you may have added through events (ACL…) are executed again, too. This may lead to side-effects that are very hard to debug, and the performance can be really bad, too.

The second reason is that it introduces a dependency from your view to the controler. Basically, the following schema is done when using “action” view helper :

controler -> view -> controler -> view.

Thirdly, when dealing with widgetized content, you need to have action (and hence, routes) for every action you may call in your “action” view helper. This means that you have to creata a lot of meaningless actions (because they are never called alone) and a lot of useless routes.

In a nutshell, this is bad (the rumor said that Matthew Weier O’Phinney really considers it as evil ;-) ). As a consequence of this, the Action view helper was completely removed from ZF 2.

The solution

Well, of course, there are solutions (once again, thanks to Robert Basic for giving me one of them !).

The Forward solution

The first solution is using the Forward controller plugin. The Forward plugin allows you, inside an action, to dispatch to another action, but without all the overhead (no routing is done). For instance, let’s say that, inside a specific page, you want to display an invoice. Because the invoice could be rendered alone, and that it makes sense to delegate this task to another controller, you could do this :

This action first create a ViewModel instance for this page (invoice-details action). You can fetch and send to the view some details that are specific to this page.

Then, I call the forward controller plugin :

The first parameter is the Controller name, and the second parameter is an array of params. Most of the time, you will set the ‘action’ key (as a consequence of this, you do not need to explicitely add a route, has the forward plugin already has everything he needs to dispatch the request).

The Forward() plugin returns a ViewModel. Finally, we add this view model as a child of the main view model. In your view, you just need to do that :

Nice and clean. Your code is well separated, it does not suffer from performance problems, and does not introduce any reference to the controller within the view.

The View Helper method

But this method suffers from a big flaw : this is perfect when your widgetized content is drawn in one, eventually two, pages.

But what if you want to draw a “Meteo” widget in all your pages ? Of course, you don’t want to “forward” in EVERY actions. This would be error prone, and make your code a lot less maintenable. For this specific case, the action helper was *seen* as perfect, as you would do this in your view :

Hopefully, there is a much better alternative in Zend Framework 2 : view helpers. The idea is to create a view helper for every “widget”. Your view helper will fetch data from either database (in this case, you have to handle the dependencies through the ServiceManager, as we will see later), or a web service, for instance. Once it has the data, it will call the “Partial” helper (or manually render the HTML directly in the view helper), and return the generated HTML.

Let’s stay with our Meteo example by creating a simple View helper for that. Our view helper will have a dependency to a “MeteoService” :

Because the View Helper has a dependency, you will have to tell the Service Manager how to handle those dependencies :

And in your views :

And voilà ! You have a nice widgetized architecture that you can reuse in all of your pages !

15 réflexions au sujet de « How to replace the “Action” helper in ZF 2 (and make great widgetized content) »

  1. Ping : Comment remplacer l’aide de vue “Action” dans ZF 2 (et faire du contenu par widget) | Un blog sur tout et rien…

    1. admin Auteur de l’article

      Hi,

      This is out of scope of this article and I didn’t write such a service. Basically, the service will be the one that contains the db mapper and return results from databases. Services are also used in controllers.

      I suggest you to have more information about this.

      Répondre
  2. ZF2-fun

    Hi Michaël!

    I don’t understand that in the class of MeteoWidget __invoke method, how call the $this->service->getTemperature($city);
    Because I can’t see refer code in this.

    Thanks your help :)

    Répondre
    1. admin Auteur de l’article

      Hi,

      As I told you the goal of this post was not to explain services. I’ve done this code just for example so I didn’t written MeteoService code, this was just an example.

      If its clearer for you, just imagine the MeteoService as a class that will call a web service to get the temperature. In other cases, it can be used to retrieve an element from database. Once again, this example is not complete it was just to demonstrate how view helpers can help to create widgetized content :) .

      Répondre
  3. Ping : Zend Framework Tutorials / Чем заменить хелпер «Action» в ZF2

  4. Maciek

    I used viewHelper solution in zf1. ViewHelpers are lightweigt, and this is nice. Second step was, wrtie my own class with additional functionality, separete view, some usefule metods like getParam:), init, cache, and i realized that my view helper became small ActionController:) Most of all code was placed in view helpers not in controllers. Controllers was only for routing purpose. It works, its simple, its fast, but is it correct aproach?

    Répondre
  5. Leon

    Out of interest I am trying to get the $this->forward()->dispatch() method to work but using ZfcTwig. The $view->addChild() works if the view is a php view, but when I change it to a twig view is does not work. Have you tried it with a twig view. Not sure how to event trouble shoot or check it.

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">