• Subscribe to the RSS feed! RSS icon
  • Subscribe by Email
  • home
  • blog
  • dev
  • Recent Posts

    • Automatically upload screenshots in XFCE4
    • Zend Framework full page cache tips
    • No more Wordpress
    • Xdebug is full of awesome
    • Creating a chat bot with PHP and Dbus
    • A year in review: 2011
    • Notes on shell scripting
    • Listening to Dbus signals with PHP
    • Configuring 2 monitors with xrandr
    • A quick note on Dojo's data grids and dojox.data.HtmlStore
  • Recent Comments

    • Robert on Zend Framework full page cache tips
    • Stephen S. Musoke on Zend Framework full page cache tips
    • David on Zend Framework full page cache tips
    • Anon on A quick note on Dojo's data grids and dojox.data.HtmlStore
    • James on Communicating with Pidgin from PHP via D-Bus
    • Robert on A Zend Framework 2 EventManager use case
    • Jowee on A Zend Framework 2 EventManager use case
    • Jurian Sluiman on A Zend Framework 2 EventManager use case
    • Jurian Sluiman on A Zend Framework 2 EventManager use case
    • djozsef on Webkonf 2011 recap
  • Tags

    php, about, random, framework, zend, example, ubuntu, blog, site, zend framework, book, conference, me, python, wordpress, apache, introduction, lamp, linux, open source, review, script, setup, signals, ape, community, contributing, dbus, dojo, events, hack, mysql, netbeans, pidgin, plugin, pyqt, security, shell, svn, talk
  • Categories

    • Blablabla
    • Development
    • Free time
    • Places on the web
    • Programming
    • Software
    • Uncategorized
  • Archives

    • February, 2012
    • January, 2012
    • December, 2011
    • November, 2011
    • October, 2011
    • September, 2011
    • August, 2011
    • July, 2011
    • May, 2011
    • April, 2011
    • March, 2011
    • January, 2011
    • December, 2010
    • November, 2010
    • October, 2010
    • July, 2010
    • June, 2010
    • April, 2010
    • February, 2010
    • January, 2010
    • December, 2009
    • November, 2009
    • October, 2009
    • August, 2009
    • May, 2009
    • March, 2009
    • February, 2009
    • January, 2009
    • December, 2008
    • November, 2008
    • October, 2008
    • September, 2008

A Zend Framework 2 EventManager use case

by Robert Basic on October 19th, 2011

With Zend Framework 2 beta 1 released yesterday and some free time to spare, I decided to finally try and tackle one of the "scariest" additions to the Zend Framework - the EventManager component. Now, I won't go into details about this whole event voodoo, Matthew already did that. Twice.

Basically, this allows us to have one piece of code to trigger an event and to have one or more listeners listening to this event. When the event gets triggered, the listeners are called and then we can do *something*, like caching or logging. Logging or caching. Caching. Logging...

See, that's my problem. All the event examples stop at logging and caching. Truly there must be some other example for which this event stuff can be used for. (Yes, I know. The whole dispatch process is now event driven or whatnot in ZF2, but I need event examples in my application, not in the framework.) I don't claim I found a perfect example for the events, but I tried.

The problem

One of the most "repetitive" places in my code I found is the save method in my models. Pass an array of data to it, possibly do something with that data, validate it, persist it, maybe do some more data mangling, return true/false to the caller. Over and over again, but just with enough difference between different models that there is actually no double code to pull out to an abstract class or some such.

Say, for example, we have a Post of some sort. It has a title and a slug created from the title. Standard stuff, nothing fancy. For the Post to be valid, it needs to have both the title and the slug set.

Now, without the EventManager, the save method could have a similar flow:

  • call the save method, passing in the data array
  • check if the data array has a slug set, if not, create one from the title
  • validate the data array, to make sure both title and slug are properly set
  • save the post

As I said, pretty standard stuff, so I'll assume you can imagine that piece of code in your head (read, I'm lazy to write it). The problem: the save method is stuffed with data preparing and validation code.

Using the EventManager

This is where, I hope, the EventManager can help. Call the save method and, just before the persist call, trigger a "pre-save" event and then persist the data. Attach two listeners to this "pre-save" event; the first will do the data preparation, the second will do the validation. There, done. Now the save method doesn't have that unneeded code around it and can be pulled out to an abstract class, all that is in the event listeners.

Let's see some code:

<?php

// This is the Post object

class Post
{
    protected $events = null;

    public function events()
    {
        if ($this->events === null) {
            $this->events = new Zend\EventManager\EventManager(__CLASS__);

            $this->events->attach('save', array('SlugifyPost', 'slugify'), 100);
            $this->events->attach('save', array('ValidatePost', 'validate'), 90);
        }

        return $this->events;
    }

    // this method can be pulled out to an abstract model class
    // and reuse it for all the models that extend it
    public function save($data)
    {
        $this->events()->prepareArgs($data);
        $response = $this->events()->trigger('save', $this, $data);

        echo 'data saved! ' . json_encode($response->last());
    }
}

I just set a "save" event to be triggered and attached two listeners to that event, the slugify and the validate methods. When the save method gets called, the event is triggered and the EventManager calls our listeners. One fairly important point here is the prepareArgs method call, which prepares the event arguments in such way, that when these arguments (the $data array in this case) are modified in one listener, this modification is reflected for the other listeners, too. If you don't want to modify the arguments in the listeners, this call can be omitted. As for the rest of the code, it's explained in Matthew's posts and in the ZF2 docs.

And here's how the slugify method modifies the data:

<?php

class SlugifyPost
{
    public function slugify($event)
    {
        $data = $event->getParams();

        $event->setParam('slug', strtolower(str_replace(' ', '-', $data['title'])));

        return $data;
    }
}

Calling the save method itself remains as it was before introducing the EventManager, which means I could add this to my models without changing the API and not break anything! Genius! Theoretically, anyway...

<?php

$post = new Post;

$values = array(
    'title' => 'My post'
);

try {
    $post->save($values);
} catch(\InvalidArgumentException $e) {
    echo $e->getMessage();
}

You can find this complete example on Github.

Thoughts?

So, what do you think? Does this approach makes sense to you? Do tell. I kinda like the idea, but time will tell in the end, as always.

Happy hackin'!

Tags: eventmanager, events, listeners, zend framework, zf2.
Categories: Development, Programming.
Comments: 4 comments.

Comments: 4

  • Jurian Sluiman

  • October 20th, 2011
Nice idea for the event manager, good post :) I am not fully convinced by your example however, for the reason the slug and validate actions are in this example not really independent from the save. What I mean, is the methods to slug and validate are purely depending on this Post object (or actually the object data). What is the benefit then? I can imagine this becomes useful if you implement it like the Doctrine listeners: you place an annotation at your entity @Sluggable and when persisting, the listener acts to all entities which are @Sluggable, removing the sluggify code out of your entity. I also came up with some more examples how your can use events: 1. Cache 2. Logging (just kidding) 3. Flushing of Doctrine's entity manager 4. Search indexes For #3: at some points in your code you might (if you use Doctrine) flush. That means, execute (sql) code to commit the changes to the database. E.g. you create three instances, persist them all and then call one time flush(). This can be an event, where other hooks can be placed to provide functionality before or after the flush (as for example, logging and cache, but also backups for rollbacks, I don't know). For #4: We're already thinking to build a content management framework on top of zf2, where search can play an important role. When you update a page or something else, you can trigger an event 'content.update' (or similar, 'content.create'). In the Event object you place the content that might be used. You can attach a listener to scan the content and place it in the search index. (Also, you can trigger a notification for users to let them know something has changed on the site.)
  • Robert

  • October 20th, 2011
Thanks for the feedback Jurian! Well, maybe not completely independent, but the code got cleaner overall, imho. If I want to change how the slug is created, I just change the slugify method, don't need to touch the save method. Or if I want to do more data preparation, I can attach more listeners, or one "general" listener, which will do that. Also, could create "independent" listeners, for example, a listener which adds a timestampable behaviour to the objects, like the one in Doctrine. As for the examples, I really like #4, a great use case indeed!
  • Jurian Sluiman

  • October 20th, 2011
Actually you gave me a new insight :) The use case of a controller cq module specific event manager might be pretty cool. I haven't found a "real" example besides your save event, but it might help to make modules more light weight and extensible. Especially with agile methods, to "throw in" some new feature (like the slug) it can be beneficial. I need to think about an event driven controller a bit more, perhaps I'll get back to this later :)
  • Jowee

  • October 22nd, 2011
Love the examples. I've been working on a 'Policy' system. Where modules can have certain policies 'AdminPasswordPolicy' etc. I was thinking that, while my model validation is all that is required for the entity to be persisted (required values of correct type) they may not necessarily satisfy the desires of the business logic. ( I know business logic is held in the model layer, but just think about it ) . We register these policies , which are really just listeners. Then when an entity triggers an event, update a password for instance, and passes it the new password data, then the policy can error or filter when the password does not meet the defined requirements (complexity etc). I think the event manager is pretty golden once everyone starts thinking outside the box.

Leave a Reply

Comment added! Will be published soon!

Robert Basic © 2008 — 2012
Design & graphics by: Livia Radvanski
Coded by: Robert Basic
Home page last updated on November 30th, 2009.
Frameworks used: Zend Framework, Dojo, 960 Grid System