A Gentle Introduction to the Zend Framework 2 Event Manager

Today I take you through the Zend Framework 2 EventManager. We’ll look at the EventManager key patterns, how it works and step through with some hands on code.


Enjoying the introduction to Zend Framework 2? I hope so. In part one of the series, we looked at Dependency Injection, otherwise know as the Inversion of Control principle.

Then, in part two, we looked at Modules and the ModuleManager, the next key aspects. In this part, we started to work through what they are and stepped through how to build one from scratch; along with some pointers for taking them further.

In this, the 3rd of 4 parts, I’m taking you through the next key aspect of the framework - the EventManager. In today’s post, I’ll be doing the following:

  1. Taking you through the EventManager key patterns
  2. Looking at how it works
  3. Stepping through using it with some hands on code

I know there’s a lot of theory behind each of these concepts, and the framework as a whole, but by having all of this foundation knowledge, the rest of the concepts become a whole lot simpler. Wouldn’t you agree?

So let’s get started with knowing how it works

It Implements The Observer Pattern

Observer Pattern Observer pattern, credit: http://en.wikipedia.org/wiki/Observer_pattern

This is the key pattern to be familiar with when understanding the EventManager. The concept is simply this:

An application has a object, which has a registered list of observers. It performs its actions as normal and notifies the observers when a specific event has occurred. These observers are then able to take action relevant to only themselves.

On the surface of it, this is a rather simple and very effective method of looking at development, I’m sure you’ll agree. It also has several advantages. These are:

  • No Monster Classes: This allows us to write code can be written with a single purpose in mind, following the Single Responsibility Principle.
  • Easier to extend: So much code starts out simply, then new requests and desires appear. With the Observer pattern, it’s simple to meet both aims.
  • Easier to test: Imagine trying to test a monolithic codebase, or one which has so many different needs and desires? When each part is simple, concise and decoupled, makes the job of testing far simpler.

It Implements Aspect-Oriented Programming (AOP)

This is one area I’m not yet totally familiar with. But it makes for really interesting reading. Wikipedia says this about Aspect-Oriented programming:

The motivation for aspect-oriented programming approaches stem from the problems caused by code scattering and tangling. The purpose of Aspect-Oriented Software Development is to provide systematic means to modularize crosscutting concerns.

The implementation of a concern is scattered if its code is spread out over multiple modules. The concern affects the implementation of multiple modules. Its implementation is not modular.

The implementation of a concern is tangled if its code is intermixed with code that implements other concerns. The module in which tangling occurs is not cohesive.

In short, I believe you can sum it up thinking about it this way: Write clean code, well organised code, with one purpose in mind. Code which doesn’t try to solve on more than one problem.

For example, if you write code which searches a datasource for information matching a query, it doesn’t attempt to render the information for the user as well.

Read this Aspect-Oriented programming series on PHPMaster for a fuller understanding.

It Has An Event-Driven Architecture

The following two statements from an article on Event-driven architecture show why it’s an excellent approach to take:

Building applications and systems around an event-driven architecture allows these applications and systems to be constructed in a manner that facilitates more responsiveness, because event-driven systems are, by design, more normalized to unpredictable and asynchronous environments.2

Event-driven architecture can complement service-oriented architecture (SOA) because services can be activated by triggers fired on incoming events.[2][3] This paradigm is particularly useful whenever the sink does not provide any self-contained executive.

To help you out a bit more, here’s an excellent, yet simple, diagram

EDA Architecture

courtesy of IBM

You can see from this definition how Event-Driven architecture, Aspect-Oriented design serve to compliment each other. And by implementing them, Zend Framework 2 allows you to write complex applications in a cohesive, yet greatly decoupled manner.

No?

Because you can write a series of components (or sub-components) all focusing on doing one, clear, task really well. These components are then linked together through listeners and are triggered (or called in to action) as needed. Not before, not after. I’d suggest this, in combination with good lazy loading makes it even simpler and more effective. Now, Matthew Weier O’Phinney may correct me here, but it seems right to me. Do you agree?

What Is The EventManager?

The Event Manager is the component of the framework which allows you to hook in to named events in your application. Not so tough, right?

I’m not trying to over-complicate it (despite the lengthy introduction). In essence, it’s a pretty simple concept, nothing more nothing less. One thing I’ve been finding, as I learn Zend Framework 2, is there are a variety of ways in which you can use it can get, seemingly, complex.

Have a look at this zend framework community thread and some of the comments on MWOP’s post on ZF2’s New Controller::init()) to get an idea of current opinion.

There are an existing set of named events in the framework, such as the dispatch event in controllers. You can also create your own as suits your application’s purpose. That’s step one.

Then, you attach (or listen) to those events. When they fire - or are triggered - your code interrogates the context of the event and responds if needed.

Not bad hey? Still with me?

Now, so you have the whole picture, you can also do the following:

  • Attach to many events at once: Need to listen on multiple events, such as logging the occurrence of all actions in a controller being fired? Easy.
  • Detach listeners: Maybe a listener is no longer required, or not available due to a service outage. Then remove it from the list.
  • Short-circuit execution: This helps you stop other listeners executing if, for example, there’s no further work to be done. It’s also handy for performance by flattening out the call stack.
  • Set priorities on events: This is handy if different listeners need to fire off earlier or later in the event process. A trivial example is form validation where you want to log information before and afterwards. The higher the number, the higher it’s priority - and the earlier it will fire.

I hope you are starting to get an understanding of just how flexible this feature is. You can create simple, reusable, modular code and interlink throughout your application.

An EventManager Example

As I said there are so many possibilities - and as I’m still learning myself - I can’t do all of them justice. Tell me what you’ve done in the comments though. So what I’m going to do is to work through a few examples.

The first one is one MWOP provided in a recent post. I was looking for a way of testing the event manager and performing simple logging whenever a controller’s action fired.

In the code below, we setup the EventManager for our controller, passing in an $events object. It’s a nice starter example as it’s so self-contained. We’re self-attaching if you will.

Ok, so after we’ve composed an EventManager instance, we then attach to the dispatch event of the current controller.

Next we extract the method used for the current request (GET, POST etc). Then, we ask if the method was outside of PUT, DELETE or PATCH or if we had no id query parameter.

If either of these conditions are met, we do nothing. If either were, we are then redirected to a named route.

public function setEventManager(EventManagerInterface $events)
{
    parent::setEventManager($events);
    $controller = $this;
    $events->attach('dispatch', function ($e) use ($controller) {
        $request = $e->getRequest();
        $method = $request->getMethod();

        if (!in_array($method, array('PUT', 'DELETE', 'PATCH'))) {
          // nothing to do return;
        }

        if ($controller->params()->fromRoute('id', false)) {
          // nothing to do return;
        }

        // Missing identifier! Redirect. return
        $controller->redirect()->toRoute("generic");
    }, 100);
    // execute before executing action logic
 }

Another Example

In this example, what I was trying to do was to get my own head around the process. I was working through basic ZF2 elements, such as forms, controllers, actions etc.

I’d already created a simple form, following along with the documentation on the ZendSkeleton application. The example I thought of was logging information about the form details both before and after the validation process.

So what I’m doing in the following example is to override and attach a listener to the isValid method. So here’s the relevant Form class code:

<?php
namespace Generic\Form;

use Zend\Form\Form;
use Zend\EventManager\EventManager;
use Zend\EventManager\Event;
use MaltBlue\Listener\Logging;

class AlbumForm extends Form {
  protected $events = null;
  protected $logger = null;

  public function events() {
    if ($this->events === null) {
      $this->events = new EventManager(__CLASS__);
      $logListener = new Logging();
      $this->events()->attach(
        'isValid.pre',
        array($logListener, 'logOutput')
      );
      $this->events()->attach(
        'isValid.post',
         array($logListener, 'logOutput')
      );
    }
    return $this->events;
  }

  public function isValid() {
    $response = $this->events()->trigger(
      __FUNCTION__ . '.pre', $this, $this->data
    );
    $isValid = parent::isValid();
    $response = $this->events()->trigger(
      __FUNCTION__ . '.post', $this, $this->data
    );
    return $isValid;
  }
  …
}

I’ve created an $events object and defined an events method. In the events method, I instantiate an EventManager object using the current class. I also instantiate a new Logging class, which is, to be fair, a poorly named class which logs to a file, the information available.

I attach then attach to the self-named isValid.pre and isValid.post, passing the logging class. I then override the isValid method, triggering the pre method before validation and post afterwards, passing in the form class and form data.

I don’t hold up execution or change it in anyway as you can see with return $isValid.

The Listener Class

<?php
namespace MaltBlue\Listener;

use Zend\Log\Writer\Stream;
use Zend\Log\Logger;
use MaltBlue\Listener\Exception;

class Logging {
  public static function logOutput($event)
  {
    $logdir = dirname(__DIR__) . "/../../data/logs/";
    $stream = @fopen($logdir . "logs.log", 'a', false);
    if (!$stream) {
      throw new Exception\InvalidFileException( 'Failed to open stream' );
    }
    $writer = new Stream($stream);
    $logger = new Logger();
    $logger->addWriter($writer);

    switch ($event->getName()) {
      case ("isValid.post"):
        list($id, $title, $artist, $submit) = each( $event->getParams() );
        $message = sprintf(
          "Post form validation: %s %s ", $title, $artist, $event->getParam('id')
        );
        $logger->info($message);
      break;

      case ("isValid.pre"):
        list($id, $title, $artist, $submit) = each( $event->getParams() );
        $message = sprintf(
          "Pre form validation: %s %s ", $title, $artist, $event->getParam('id')
        );
        $logger->info($message);
      break;
    }
  }
}

The function, logOutput, above receives an EventManager object and initialises a Zend Log object adding a stream writer. I then have a simple switch statement looking at the $event object.

It uses one of the methods of the object, getName, to determine what event just fired. If it’s either isValid.post or isValid.pre, then it takes action, extracting the information passed in through the getParams method. It trivially logs some of the information to a log entry.

In this, slightly longer example, you can see more of the functionality available. You can see how:

  • We can trigger events as required
  • Create our own named events suiting our purpose
  • Know what event was fired
  • Look at the information in the event

All without extending a class needlessly and poorly. I could have taken a lot of different types of action here. I could have:

  • Sent a tweet
  • Sent an email
  • Displayed further information

However, I’ve kept it intentionally simple as my intent is to introduce the EventManager, not give it an exhaustive run through.

I’ve provided a list of helpful and valuable links in the further reading section below to help you see how you can use it in a range of forms.

What Are Your Thoughts on the Zend Framework EventManager?

What I’ve written here is my take on how the EventManager works and understanding of the key concepts which compose it. However I’m still learning about it too and would love to hear your understanding about it.

Do you believe it’s written the write way and really does allow us to develop cleaner, more effective and scalable applications? Feel free to share your experience and knowledge in the comments below so we can all better use the EventManager in ZF2.

Further Reading


You might also be interested in these tutorials too...

Thu, Jun 27, 2013

\Zend\Db\Sql - Creating Joins and Unions with Ease

In part two of the \Zend\Db\Sql\Select series, we create inner, outer, left and right joins as well as union queries with ease. Come learn more about building queries simply, effectively and easily in your every day applications with Zend Framework 2.

Tue, Jun 26, 2018

What Are Delegator Factories and Why You Should Use Them

Ever wanted to dynamically expand the functionality of an object which you retrieve from your dependency injection container, based on different needs, yet without creating messy, hard to maintain configurations? Then you’re going to want to know about a powerful new technique - called Delegator Factories.

Tue, Jul 25, 2017

How to Create a Zend Expressive Module

Ever thought of creating a Zend Expressive module, one that either scratches an itch or allows you to use a set of related functionality across multiple Zend Expressive projects? I created one recently and share how I did it with you in this three-part series.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus