Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Dependency Injection and Dependency Container With an Event Dispatcher

After reading this great article on dependency injection : http://miller.limethinking.co.uk/2011/07/07/dependency-injection-moving-from-basics-to-container/

I realized that my small framework was alrready using a Denedency Injection Container for the most part.

I have an App class with manage creation of all services Database, Config, Session, and that is able to instanciate of diferent classes.

But I realised that one part was causing issues I pass an Event dispatcher to a lot of base classes in my framework, but each of theses classes use the dispatcher like:

$this->dispatcher->fire(new FB_Event('FB.Database.Model.beforeUpdate', $this, array('pk' => $pk, 'row' => $row)));

So as you see I use a new there so i'm not able to fully unit tests my classes. I don't want to pass my App container to all application because it's a nightmare to debug after since the App contains instances of the Database, Session and a multitude of services that the other classes don't need.

What is the best way of resolving this since I cannot pass an FB_Event through dependency injection, since the same function usually fire 2 event:

public function delete($pk) {
    $e = $this->dispatcher->fire(new FB_Event('FB.Database.Model.beforeDelete', $this, array('pk' => $pk)));
    $pk = $e->params['pk'];
    if ($e->preventDefault()) {
        return false;
    }

    $this->requirePK();
    list ( $where, $params ) = $this->pkParts(null, $pk);
    $sql = "DELETE FROM {$this->name} WHERE $where";
    $stm = $this->db->execute($sql, $params);

    $this->dispatcher->fire(new FB_Event('FB.Database.Model.afterDelete', $this, array('pk' => $pk)));
    return!(bool) $stm->errorCode();
}

One of my solution is to add a new function to the dispatcher called fireEvent wich will take care of creating the new FB_Event wich will remove the coupling everywhere. But will add coupling between the Dispatcher and the Event wich is less bad.

public function fireEvent($name, $target, $params = array()) {
    return $this->fire(new FB_Event($name, $target, $params = array()));
}

Would you create some sort of EventFactory in the container and pass it to the Dispatcher ? Is this overkill to remove dependency to a small object like Event ?

Thanks for your advice

like image 812
inkubux Avatar asked Dec 07 '25 05:12

inkubux


2 Answers

I actually think your suggested solution is the best answer. You're right in that instantiating an FB_Event in the code is to be avoided as it makes it hard(-er) to test and couples your code. Yes in your fireEvent method there is coupling between the Dispatcher class and the Event class but the Dispatcher should know about Events as that is what it is dispatching. Yes, coupling is to be avoided but some classes do need to know about other classes sometimes; coupling can't always be avoided. The good thing about your fireEvent method is that it's a seam and hence could be used to break code up in future.

like image 116
liquorvicar Avatar answered Dec 08 '25 20:12

liquorvicar


What is the best way of resolving this since I cannot pass an FB_Event through dependency injection, since the same function usually fire 2 event:

You can work around this problem by using setters in your FB_Event class. For example, pass an instance of FB_Event (DI) to concerned class then use it as:

public function delete($pk) {

    $this->fb_event->setName('FB.Database.Model.beforeDelete');     
    $this->fb_event->setTarget($this);
    $this->fb_event->setParams(array('pk' => $pk));

    $e = $this->dispatcher->fire($this->fb_event);
    $pk = $e->params['pk'];
    if ($e->preventDefault()) {
        return false;
    }

    $this->requirePK();
    list ( $where, $params ) = $this->pkParts(null, $pk);
    $sql = "DELETE FROM {$this->name} WHERE $where";
    $stm = $this->db->execute($sql, $params);

    $this->fb_event->reset();
    $this->fb_event->setName('FB.Database.Model.afterDelete'); 
    $this->fb_event->setTarget($this);
    $this->fb_event->setParams(array('pk' => $pk));    

    $this->dispatcher->fire($this->fb_event);
    return!(bool) $stm->errorCode();
}
like image 36
Vikk Avatar answered Dec 08 '25 20:12

Vikk