Symfony EventsEvery event happens for a reason
Brent Shaffer@bshaffer
CentreSource Interactive Agencywww.brentertainment.com
June 1st, 2010
Symfony Events“All [symfony] events are blessings given to us to learn from.” - Elizabeth Kubler-Ross
“Great [symfony] events make me quiet and calm; it is only [fat controllers] that irritate my nerves.”- Queen Elizabeth
“Keep [symfony events] around for when shit gets crazy. Cause shit will get crazy!”
- Carp Guy
• Symfony events are part of the Event Dispatcher component
• Implements the Observer design pattern (Design Patterns, GoF)
• “...The subject maintains a list of its dependants, called observers, and notifies them automatically of any state changes”
• Lightweight, consists of only 2 classes
1. sfEventDispatcher - object responsible for maintaining a register of listeners and calling them whenever an event is notified
2. sfEvent - object that stores information about the notified event
Overview
What are Symfony Events
Notify Until - Notifies all listeners of a given event until one returns a non null value.
Filter - Filters a value by calling all listeners of a given event.
Notify - Notifies all listeners of a given event.
sfEventDispatcherConnect - Connects a listener to a given event name.
$dispatcher->notifyUntil(sfEvent $event);
$dispatcher->notify(sfEvent $event);
$dispatcher->filter(sfEvent $event, $value);
$dispatcher->connect('event.name', array($php, 'callable'));
// sfContext and sfApplicationConfiguration:$this->context->getEventDispatcher();sfApplicationConfiguration::getActive()->getEventDispatcher();
// sfFormSymfony subclassesself::getEventDispatcher();
// sfActions subclass / sfBaseTask subclass / sfPHPView:$this->dispatcher
How do I access it?
sfContext and sfApplicationConfiguration hold a single sfEventDispatcher instance and facilitate it to other classes
sfEventDispatcherWhere is it?
sfEvent
public function __construct($subject, $name, $parameters = array())
Subject
Name
Parameters
The subject of the event (the object notifying the event, or object the event is about. It can also be null);
The event name
An array of parameters to pass to the listeners (an empty array by default)
The event object can also be accessed as an array to get its parameters
$method = $event['method'];// Is the same as$parameters = $event->getParameters();$method = $parameters['method'];
Constructor
NotifyA client calls in for Employee One to call them back// aClient.class.php$this->dispatcher ->connect('employee_one.becomes_available', array($this, 'callMe'));
A lawyer calls in, needs Employee One to sign some lawyer stuff
Receptionist with Four Employees
12 3
4
Receptionist (event dispatcher)
// EmployeeOne.class.phppublic function becomesAvailable() { $this->dispatcher->notify(new sfEvent($this, 'employee_one.available'));}
Employee One lets secretary know when he’s available.
// aLawyer.class.php$this->dispatcher ->connect('employee_one.becomes_available', array($this, 'signMyStuff'));
Conceptual Example
Conceptual Example Notify
Employee Three wants to know when employee 2 leaves, so he can eat his leftovers// EmployeeThree.class.php$this->dispatcher->connect('employee_two.goes_home', array($this, 'stealLeftovers'));
public function stealLeftovers(sfEvent $event) { $employeeTwo = $event->getSubject(); $this->eat($employeeTwo->getLeftovers());}
// EmployeeTwo.class.phppublic function goesHome() { $this->dispatcher->notify(new sfEvent($this, 'employee_two.goes_home'));}
Employee Two lets secretary know when he’s leaving.
Receptionist with Four Employees
12 3
4
Receptionist (event dispatcher)
Conceptual Example
Employee 4 puts out an add in the newspaper
Now Hiring: Personal assistant. Strong communication skills.
Willing to take excessive sexual harassment. Call before 5:00 PM
Individuals call in to register// aProspect.class.php$this->dispatcher->connect('employee_four.job_offering', array($this, 'callMe'));
// aFeministMovement.class.php$this->dispatcher->connect('employee_four.job_offering', array($this, 'sue'));
// EmployeeFour.class.php (at 5:00)$event = $this->dispatcher->notifyUntil(new sfEvent($this, 'employee_four.job_offering'));if($event->isProcessed()) $employee = $event->getReturnValue();
Employee Four notifies the registered listeners until he finds an employee
Notify Until Receptionist with Four Employees
12 3
4
Receptionist (event dispatcher)
Conceptual Example
Employee 4 puts out an add in the newspaper, yadda yadda, but this time, he wants a list.
// EmployeeFour.class.php (at 5:00)$event = $this->dispatcher->filter(new sfEvent($this, 'employee_four.job_offering'), $list);$list = $event->getReturnValue();
// aProspect.class.php$this->dispatcher ->connect('employee_four.job_offering', array($this, 'addToList'));
public function addToList($event, $list) { $list[] = $self; $event->setReturnValue($list);}
Employee Four notifies the registered listener and provides the item to filter
Filter Receptionist with Four Employees
12 3
4
Receptionist (event dispatcher)
Utilizing Symfony Core EventsLazy-add mobile templates
We will now demonstrate how useful symfony core events can be in your application
We will show how to add mobile-specific layouts to your existing application.
“Lazy-add” - add mobile templates as you go while keeping everything without a mobile template still accessible. In other words, if a mobile template exists, render it for mobile devices. Otherwise, render the default template.
Based largely on the blog post “How to create an optimized version of your website for the iPhone in symfony 1.1”
Utilizing Symfony Core EventsLazy-add mobile templates
Connect to “filter parameters” event, add request format for the appropriate user agent
// ProjectConfiguration.class.phppublic function setup() { ... $this->dispatcher->connect('request.filter_parameters', array($this, 'filterRequestParameters'));}
public function filterRequestParameters(sfEvent $event, $parameters){ $request = $event->getSubject(); if (preg_match('#Mobile/.+Safari#i', $request->getHttpHeader('User-Agent'))) { $request->setRequestFormat('m'); } return $parameters;}
Utilizing Symfony Core EventsLazy-add mobile templates
Connect to “view.configure_format” event to only set if the template exists
// ProjectConfiguration.class.phppublic function setup() { ... $this->dispatcher->connect('view.configure_format', array($this, 'configureMobileFormat'));}
public function configureMobileFormat(sfEvent $event){ if ('m' == $event['format']) { $view = $event->getSubject(); $dir = sfConfig::get('sf_app_module_dir').'/'.$view->getModuleName().'/templates/'. $view->getActionName().$view->getViewName().$view->getExtension();
if (!file_exists($dir)) { $view->setExtension('.php'); } }}
Utilizing Symfony Core EventsLazy-add mobile templates
Set our mobile format in factories.yml
// factories.ymlall: request: class: sfWebRequest param: formats: m: text/html ...
Now, we can add mobile templates as we go, while keeping the rest of the site as-is!
Creating Your Own EventsAlert System
// ProjectConfiguration.class.phppublic function setup() { ... include_once sfConfig::get('sf_lib_dir').'/alert/sfAlertDispatcher.class.php'; $this->alertDispatcher = new sfAlertDispatcher(); $this->alertDispatcher->connectEvents($this->dispatcher);}
We will now demonstrate how creating your own set of events can be useful.
Using events keeps your libraries modular and decoupled. It also makes them very easy to unit test.
You want to build a system in your application that sends email alerts out for various triggers. You’re smart, so you will accomplish this using the event dispatcher
Creating Your Own EventsAlert System
// lib/alert/sfAlertDispatcher.class.php class cfitAlertDispatcher{ public function connectEvents(sfEventDispatcher $dispatcher) { // = Raise Alerts = $dispatcher->connect('contact.form_submitted', array($this, 'alertContactForm'));
$dispatcher->connect('support.ticket_submitted', array($this, 'alertTicketSubmitted')); // = Resolve Alerts = $dispatcher->connect('support.ticket_closed', array($this, 'resolveSupportTicket')); }}
Handle your events inside your model. Keep things modular.
Creating Your Own EventsAlert System
// lib/alert/sfAlertDispatcher.class.php class cfitAlertDispatcher{ ... public function alertContactForm(sfEvent $event) { // ...do alert logic, etc $form = $event->getSubject(); $this->mailer->send($this->getContactEmailFromForm($form)); }
public function alertTicketSubmitted(sfEvent $event) { // Notice how easy this logic will be to unit test! $ticket = $event->getSubject(); if($ticket->isAtleastCritical()) { $this->mailer->send($this->getEmailFromTicket($ticket)); } } ... }
Write your implementation logic for the appropriate listeners
Creating Your Own EventsAlert System
// contactActions.class.php ...$this->dispatcher->notify(new sfEvent($form, 'contact.form_submitted'));
// ticketActions.class.php...$dispatcher->notify(new sfEvent($ticket, 'support.ticket_submitted')); ...// Ticket.class.phppublic function close(sfEventDispatcher $dispatcher){ ... $dispatcher->notify(new sfEvent($this, 'support.ticket_closed'));}
Add the logic in your application for triggering the events
Creating Your Own EventsAlert System
And that’s it! The rest is a matter of implementation logic for your specific application
Notice how easy cfitAlertDispatcher is to unit test. Get rid of all those fat controllers!
And one last quote...
“...Gather in your resources, rally all your faculties, marshal all your energies, focus all your capacities upon mastery of [symfony events]”- John Haggai
2010-05-01
Brent Shaffer@bshaffer
CentreSource Interactive Agencywww.brentertainment.com
Questions?
Top Related