Returning From Triggered Events: An Event Manager Example - Another Zend 2 Snippet

I've recently had a bit of a rushed initiation to Zend 2's event manager to get some code completed for a deadline.

To be honest there's not an awful lot to what I've looked at so far; more towards the simpler end of things...but not all of it is obvious despite some pretty good documentation out there: you'll find much worse starting places than this very well written blog.

So What's The Problem?

What I needed to do was to "short circuit" (as they call it) the event listener if the methods called by the event actions return some bad result.

There's lots written, all much the some, some copied directly from the Zend 2 documentation and passed off as their own work (you know who you are) about short circuiting the event listener, but no practical examples, so here's one:

What's I'm Trying To Achieve:


Basically I'm got contacts who can have multiple addresses. If an address is marked as being their default address (or their only address, it's the same thing) then it shouldn't be deleted. When a user tries to delete an address these things need to be checked before I allow it.

Deleting the whole user is obviously a different matter, and there's a lot of code I've stripped out of this example to simplify it.

The Setup:


I'm not going massively into the setup. I've basically used something very similar to the examples in the docs anyway, but for completeness:

Inside my class class I've declared:
protected $events;

The class also contains a setter and getter for the event manager (as said, almost identical to the docs):

The getter:

public function getEventManager()
{
    if (null === $this->events) {
        $this->setEventManager(new EventManager());
    }
    return $this->events;
}

The setter is slightly more involved. Here we set which actions to pass to the listener, and what we parse back as a result of these actions. Here's the method:

public function setEventManager(EventManagerInterface $events)
{
    $events->setIdentifiers(array( __CLASS__, get_called_class() ));
    $obj = $this;

    $events->attach('delete', function($e) use ($obj) {
        $result = false;
        $result = $obj->canDelete();
        return $result ? null : true;
    }, 100); // this last int represents a priority for the event - see the Zend 2 docs!

    $this->events = $events;
    return $this;
}

So what's going on? Firstly we set which calling object is of interest to the event manager in this class, and then we add some events! Because the event we're attaching consists of a closure
$this
is out of scope, hence parsing in
$obj
Now, from inside this event we can return the value returned by the called method, in this case canDelete():

/**
* check whether address details can be deleted
*/
public function canDelete()
{
    if ($this->getdefaultAddress() == 1) {
        return false;
    } else {
        return true;
    }
}

This method returns either true if we can delete, or false if not. If we return false we want to stop propagation inside the event listener, and so much return true from this. At first look this is slightly unintuitive, but makes good sense from the context of the event listener itself, and explains the
return $result ? null : true;
inside the event call. Basically, if the method returns false we need to parse true to the end listener to stop whatever action we were checking for.

To make all this work we need to have a trigger in the delete method:

public function delete()
{
    $result = $this->getEventManager()->trigger(__FUNCTION__, $this, array(), function($e){return $e;});
    if(!$result->stopped()) {
        return parent::delete();
    } else {
        return false;
    }
}

This, again, is very close to the trigger setup in the docs. Parsing __FUNCTION__ as the function name for the trigger is just good practise (if you're calling the function you're in of course). We assign the trigger to a variable and can check the returned value from the event listener. If canDelete() returned false, we parsed true to the event listener, and stopped() is returned as true, allowing us to halt the action inside the method.

Comments

Popular Posts