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:
- Taking you through the EventManager key patterns
- Looking at how it works
- 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, 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
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
Join the discussion
comments powered by Disqus