Zend Framework 2 Event Manager

In this article, we begin by introducing the high-level concepts of the event system that is a central part of Zend Framework 2. Afterwards, we will give you a simple example of attaching an event listener to an event and how to trigger an event.

The Event System

A fundamental principle is that Zend Framework 2 is based on the Event-driven Architecture (EDA). Simply put, the idea is that activities occur in response to events. Thus, activities are attached to events, and when a given event occurs, its attached activities are executed. This principle is well known in programming; Java and the .NET platform, for instance, both make heavy use of event handling.

Imagine placing an order on Amazon. When the order is placed, a series of activities or processes should occur; a confirmation should be sent to the customer, the items should be picked from the warehouse, and the customer’s credit card should be debited. These processes are registered on the OnOrderPlaced event, and when this event is triggered, the attached processes or activities are invoked.

This approach gives a great deal of flexibility in a system. It is easy to add or remove activities that should happen when a given event occurs. Thus, business processes can easily be adapted to changing business needs.

On the other hand, it can be hard to understand what actually happens during a business process. Activities can be attached to events almost anywhere in the application, and perhaps even added dynamically. This makes it difficult to both understand and debug a process; one must know which activities are attached to an event at runtime to understand exactly what happens. The flow of the business process is therefore less transparent than if it were implemented in a completely procedural fashion.

A thing that further complicates this approach is the sequence in which activities are to be executed. Activities may depend upon each other; for instance, a customer’s credit card should not be debited before the items are picked from the warehouse.

The result of these disadvantages is that business processes are harder to understand, but it has been concluded by the Zend Framework team that the great extra flexibility outweighs this. As it turns out, the framework makes use of events internally, e.g. in the module manager.

The Event Manager

The event manager is mainly used to attach event listeners to events and to trigger events. It can be thought of as an implementation of the observer pattern where the event is the subject and the observers are the attached event listeners. An example of triggering events is given below.


use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;

class UserMapper implements EventManagerAwareInterface {
	public function setEventManager(EventManagerInterface $events) {
		/* ... */
	}
	
	public function getEventManager() {
		/* ... */
	}

	public function register(User $user) {
		$this->getEventManager()->trigger(__function__ . '.pre', $this, array(‘user’ => $user));
		
		// Register code here
		
		$this->getEventManager()->trigger(__function__ . '.post', $this, array(‘user’ => $user));
	}
}


The first argument passed to the trigger() method is the event name to trigger. In this example, events with the names register.pre and register.post are triggered. The second argument is the target, which will often be the class from which the event is triggered. The last argument is an array of data that will be passed to the event listener and can be used for various purposes such as logging.

Attaching event listeners is a simple matter; an event listener is simply a PHP callback that takes an event object as its single parameter. This object enables developers to get information about the event that was triggered such as its name and any data passed with it.


use Zend\EventManager\EventManager;
use Zend\EventManager\EventInterface;

$user_mapper = new UserMapper();
$user_mapper->setEventManager(new EventManager());

$user_mapper->getEventManager()->attach('register.post', function(EventInterface $event) {
	$logger = new \Zend\Log\Logger();
	$logger->addWriter(new \Zend\Log\Writer\Stream('php://output'));
	
	$logger->log(\Zend\Log\Logger::INFO, 'A user with the ID: ' . $event->getParam('user')->getUserID() . ' has registered');
});


In the example above, an event listener is added to the register.post event. A callback function in the form of a closure or anonymous function is used. This function creates a log entry indicating that a user has registered and accesses the user object through the getParam() method. This method provides access to any parameters passed when the event was triggered. In this case, the User object that was passed in the previous example is accessed.

A priority can also be specified when attaching an event listener to an event. The priority is simply an integer; the listener with the highest number is executed first.

2 Comments

  1. Jonecir said:

    Would you mind to post an example showing how tro trigger an event after a user selects an item from a combo box?

    February 19, 2013
    Reply
    • Andy said:

      Hello Jonecir. You would do this with JavaScript because when the page has been rendered by PHP and sent off to the user, an event cannot be triggered in PHP without establishing a connection again. Thus, you would probably want to send an asynchronous HTTP request (AJAX) to your server when the selected option changes. To handle such a change, the .change() method in jQuery can be used. For other libraries (or simply JavaScript), I will refer you to Google.

      The AJAX request will map to a controller action, in which you can trigger events if you wish to. In your controller, you can access the event manager directly through the $this->events variable or use the $this->getEventManager() method. On the event manager, you can trigger events just like shown in this article.

      I hope that helps.

      February 19, 2013
      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *