Events in CakePHP – I really like it
I am playing around with a custom “events” implementation i just finished and i must say that this is really beautiful. Okay, you have more files to manage, but in exchange you get cleaner main files (controllers, models, etc.). So yeah – it’s also a second abstraction level, but i think a very powerful one and worth to look into.
Think that you never ever again need to overwrite beforeFilter just to tell Auth what to allow in one controller. Or how Models could talk to each other. You can overwrite and apply events like there’s no tommorow too, and thus seperate tedious side-logic from the central businesslogic.
How i did it
As convenient and close to cake conventions as possible i guess. I have a app_controller_events.php and app_model_event.php file in my “app” folder, and from those i extend any custom event listener classes while keep the file names they belong to. Those classes are stored in “/app/events” and seperated in folders “controllers” and “models”. Can you already see where this is heading? :)
For controllers i wrote a EventComponent that is a wrapper to the EventDispatcher instance which sits in the ClassRegistry right from the boot. So i can get the instance anywhere in CakePHP. Now upon EventComponent::initialize($controller) i use the Inflector to make up my filenames based on Controller::$name and build a path to the event class. That looks something like this then:
/app/events/controllers/users_controller.php
If the file doesnt exists, simply nothing happens. Meaning that there is no need to create the file just because a controller exists. Same goes for event methods. If they don’t exist then the dispatch wont make your app fail. Clean and unobstrusive.
Besides the default events like beforeFilter, beforeRender etc.. i added a “per action” version of those too. Pretty cool. Besides the controller wide “beforeFilter” i now have a special one for the method. It goes by “beforeFilter_view”, for example.
class UserControllerEvents extends AppControllerEvents {
var $name = 'UserController';
function onBeforeFilter($event) {
$event->Controller->Auth->allow('register');
}
}
If i want to dispatch from anywhere in my register() action for example, i could just write:
$this->Event->dispatch('userRegistrationSaved', array('user_id' => $this->User->id));
Now if i want to do something in my UserController event-handler i just add a method by that name and prefix it with “on”.
class UserControllerEvents extends AppControllerEvents {
var $name = 'UserController';
function onUserRegistrationSaved($event) {
$event->Controller->log('New registration: User.id = '.$event->user_id);
}
}
And so forth .. For models it is practicly the same. The fun thing is that i extend from the Events base implementation aswell. So i can dispatch even more events from one event (i.e. grouping of related events).
I still have to investigate and find more use cases. I don’t want to replace the controller actions by event handlers, so one needs to be careful what he is adding as a event. This can be neat if done well.
Once i find more practical use and have the time i will release some code. Any comments on the subject are welcome. Maybe you have some more ideas for this.. or you don’t like it all. Could be either. Let me know! :)


Hm, how does Event::dispatch() in your example know it has to call UserControllerEvents::onUserRegistrationSaved()? Or does it check all event classes whether they implement onUserRegistrationSaved()?
Daniel Hofstetter said:
Yes, all classes are notified if they are callable.
Sounds very interesting. I have a different approach where Events would be a good idea. I will compare my solution to yours with some real-world examples i have and then let you know which one seems to be a better fit (at least for me).
Great work, Thank you for sharing.
More on possible Events in CakePHP…
As a follow up to: Events in CakePHP – I really like it
Let’s say you have unkown entities in your project – totally dynamic. Let it be a plugin, another model, not associated at all. Getting new functionality into an existing app can be a pain….