May 30th, 2014 · php zend framework 2

Sharing context between separate EventManager triggers

When building out the service layer I like to wrap each action in event manager triggers so I can extend it's behavior later from other modules without introducing a direct dependency. One tidbit that the Zend\EventManager docs only mention in passing is that the argv parameter of EventManagerInterface::trigger isn't restricted to array - it can be an object as well. With one simple deviation from the documented example we can share state between our service class and all event listeners: pass an ArrayObject:

$argv = new \ArrayObjecct();
$argv->arg1 = '...';
$argv->arg2 = '...';

$em->trigger("my.event", $this, $argv);

Now, when an event listener makes a change to one of the arguments passed into argv of the trigger call the service class will retain those changes so you can use them later.

Here's a generic example of what the update method of a service class generally look like:

<?php
class MyService
{
    public function update($entity, $data)
    {
        $em = $this->getEventManager();
        $form = $this->getUpdateForm();
        $mapper = $this->getRepository();

        $argv = new \ArrayObject();
        $argv->form = $form;
        $argv->entity = $entity;
        $argv->data = $data;

        // Use a Zend\Form to hydrate the entity
        $em->trigger("update.form.pre", $this, $argv);
        $form->bind($entity);
        $form->setData($data);
        $isValid = $argv['formIsValid'] = $form->isValid();
        $em->trigger("update.form.post", $this, $argv);

        // Short circuit if validation failed
        if ( ! $isValid ) {
            return false;
        }

        // Extract the hydrated entity and save it
        $entity = $form->getData();
        $em->trigger("update.save.pre", $this, $argv);
        $mapper->save($entity);
        $em->trigger("update.save.post", $this, $argv);

        return $entity;
    }
}