This title was written for Laravel version 4. If you're looking for material on the latest version of Laravel, then please check out Code Smart.
Dear handsome reader,
You have been formally invited to Dayle Rees' happy panda party. Please dress suitably and bring the enclosed coloured flags. More will be explained at the event.
Kind Regards,
Lushui.
Chief panda butler esq.
Exciting times, right? You arrive at Code Bright Manor wearing your Sunday best with coloured flag in hand. In the grand hall you are surrounded by thousands of other handsome developers. On the walls are oil paintings of famous red pandas that have served the Rees family throughout history.
Champagne and caviare are served by red panda butlers wearing their finest orange and white tuxedos. It's a really swish event. I mean, I'm a guy of good tastes, right?
I suppose it's time that I address my guests? Let me step up to the panda podium.
Welcome, beautiful people, to the annual panda party. You have all been invited due to your own fantastic taste in technical writing.
Now, I know that small-talk at these types of events can be a little awkward for developers. Fear not! I have devised a clever system. I hope that you have all brought your coloured flags along with you?
- cheers *
Excellent! I'm glad to see that you're all so enthusiastic.
Here's how it's going to work. If you would like to small-talk about the weather, then please hold up your red flag. If you would like to small-talk about recent sporting events, then please hold up the blue flag. Finally, if you would like to small-talk about video games, then please hold up the green flag.
With this simple system in place we are all free to enjoy ourselves this evening. Please help yourself to champagne and let's drink to your future projects with the Laravel framework.
Concept
So what was that all about? Well, apart from a little fun, it serves to illustrate the basics of event driven programming. By holding up a coloured flag we are 'firing' an event. Here's an example.
Event::fire('raise.blue.flag');
Other people can 'listen' or 'observe' this event. Here's how that would look.
Event::listen('raise.blue.flag', function()
{
return new SmallTalk::make('sports');
});
With our event listener in place, as soon as someone raises a blue flag we automatically initiate small talk about sports with the person. It's a very efficient process. We can register as many listeners as we like. We can even register multiple listeners to the same event.
So how do we employ a system such as this within Laravel? Well, it's exactly as in the previous examples. Ok, I lied again, a little. That SmallTalk
class doesn't exist. The rest is just fine though! Why don't we take a closer look?
Firing Events
Events are fired with a 'key'. We can use this key to register listeners later. We fire the events using the fire()
method on the Event
class. The first and only required parameter is the key itself. Let's take a look at an example.
Event::fire('my.event');
Any listeners that have been registered will be executed when this line of code has been run.
It's useful to be able to be notified in this way, but we can do much more with this eventing system. We can pass additional information along with our events. Our listeners can respond to this information, or may even modify it.
Let's take a look at another example.
Event::fire('my.event', array($object, $value));
With an optional second parameter, we can pass an array of PHP variables that will be handed to registered listeners. The standard rules of PHP apply. Objects themselves are passed directly, and basic types are passed as a copy.
This means that if you place an object or class instance into the second parameter array, then the listener will be able to modify it.
Let's take a look at the other side of the equation shall we? It's time to register some listeners.
Listening for Events
Listening for an event is simple. We can use the Event::listen()
method. The first parameter is the 'key' of the event that we wish to respond to. The second parameter is a Closure that can be used to respond to the event.
As always, let's take a look at an example.
Event::listen('my.event', function()
{
// Perform some action.
// Update the database?
});
Within the Closure we can perform any action that we need to. We could log that an event occurred. We could update a database model. Anything is possible. Use your imagination!
If our event is fired with with additional parameters then we can capture them by placing place-holder parameters within our Closure. Let's take a look at this in action.
Event::listen('my.event', function($first, $second)
{
// Use $first and $second.
});
The parameters are provided in the same order as they are represented within the array passed to the event firing method.
As I mentioned earlier, we can register multiple listeners to a single event. All registered listeners will be executed. Here's an example.
Event::listen('my.event', function()
{
// First listener.
});
Event::listen('my.event', function()
{
// Second listener.
});
Event::listen('my.event', function()
{
// Third listener.
});
Event::fire('my.event');
Within the above example, all listeners will be executed once the my.event
has been fired.
Maybe we have dealt with the event within the first listener? What if we don't want the other listeners to be processed? Not a problem! If we return a boolean false
value from our listener, then we will break the event chain and subsequent listeners will not be executed.
For example, within this code snippet...
Event::listen('my.event', function()
{
// First listener.
return false;
});
Event::listen('my.event', function()
{
// Second listener.
});
Event::listen('my.event', function()
{
// Third listener.
});
Event::fire('my.event');
... only the first event listener will be executed.
Now I'm sure you're getting tired of me telling you this, but wherever there's a Closure, you could also use a PHP class. The syntax is the same as it always is. First you define your event listener class.
class MyListener
{
public function process()
{
// Handle the event here.
}
}
Next you register the listener using a notation that's similar to how you define a controller and action pair. Here's an example.
Event::listen('my.event', 'MyListener@process');
The second parameter to the listen()
method is now the class and method pair. If you don't provide a method, then the event system will automatically look for a method named handle()
.
Events can also be subscribed to with a priority. This way we can change the order used to execute our listeners. Here's an example.
Event::listen('my.event', function()
{
// First listener.
}, 1);
Event::listen('my.event', function()
{
// Second listener.
}, 3);
Event::listen('my.event', function()
{
// Third listener.
}, 5);
Event::fire('my.event');
By specifying an integer as the third parameter of the listen()
method we can change the order that our listeners are processed. Listeners with a higher integer value will be executed first. In the above example the listeners will be executed in reverse order. Nice and simple!
When registering events, you don't have to be exact about your event key. If you like, you could use a wildcard (*) within your listener to register for a subset of events. Here's an example.
Event::listen('my.*', function()
{
// ..
});
The event listener will now be executed once any event that starts with the prefix my.
is fired.
Event Subscribers
In the previous chapter I shared some locations where you could register your code. With events you have an additional option. An event subscriber can be used to create a class that will handle multiple events.
Here's an example of a simple event subscriber.
class MyListeners
{
public function firstListener()
{
// First event listener.
}
public function secondListener()
{
// Second event listener.
}
public function thirdListener()
{
// Third event listener.
}
public function subscribe($events)
{
$events->listen('first.event', 'MyListeners@firstListener');
$events->listen('second.event', 'MyListeners@secondListener');
$events->listen('third.event', 'MyListeners@thirdListener');
}
}
Our event subscriber is similar to our standard event class. It has a number of listener methods, but also a new method named subscribe()
. This method accepts an eventing instance, which we have called $events
in the example above.
In previous examples we used the Event
class to fire and listen for events, do you remember? Well this instance is exactly the same. We can use it to register our listeners from within our subscriber class.
To allow for our subscriber class to function correctly, we need only provide a call to the subscribe()
method with an instance of our subscriber class, like this.
Event::subscribe(new MyListeners);
Whichever method you choose to register your events, I'm sure it will work fantastically for you. Now have another sip of champagne. It's on me!
Global Events
Want to hear another secret? This is just between you and me. I don't want you sharing this with the other developers, okay? We can spy on Laravel.
That's right. We can intercept its messages if we choose. You see, Laravel fires its own events. For example, when Laravel executes an SQL query the illuminate.query
event is fired. Remember that illuminate
is the codename for Laravel's component suite.
The parameters passed with the illuminate.query
event are related to the query itself. Which includes the SQL query that is to be executed.
Laravel also fires an illuminate.log
event when a new call to the Log
class is made. By interrogating the parameters that are passed to the event we can easily intercept any messages that are logged by the system.
There are also several events that are so useful that Taylor has provided some simple short-cuts that can be used to listen for them.
If you pass a Closure to the App::after()
method, then the Closure will be executed after the framework has completed it's request-response cycle. Just before the response is sent to the client.
The current request and response are sent as parameters to the event, which means you have a 'last-chance' to alter the response before it is sent. Here's an example.
App::after(function ($request, $response) {
$response->headers->set('Access-Control-Allow-Origin', '*');
});
In the above example we modify the $response
parameter to add the 'Access-Control-Allow-Origin' header. This head will now be served along with every response from the system.
You also have access to the App::before()
method that is an event which is triggered before the routing has been performed. This event only passes the current request as a parameter.
Stubs for these methods are found within the example filters.php
file that ships with the framework. While they exist in the filters file, they are actually implemented in a fashion that is more similar to an event.
Use Cases
I certainly encourage you to use the event system in a creative manner to solve your own problems, but here are some example use cases that I have found useful.
Logging & Audit
When a specific function relating to my application occurs, I like to fire an event. Here are some examples of the events that might be fired.
- user.created
- user.deleted
- profile.updated
Should I wish to log this information, forward it to an external service for analytical purposes, or event audit actions within the system, I need only register an event subscriber to watch for these defined events.
The overhead for firing an event is rather small, so I tend to place them wherever is useful. You never know what kind of actions you wish to track at a later date.
Hooks
With software pages similar to CMS's or task management systems, or "those kind" of re-distributable packages you will often find a way of 'hooking' your own extensions into the code. Or modifying the objects that the system works with.
Events are a fantastic way of allowing developers to mod on top of your system without having to extend classes.
This chapter has been in preparation for an upcoming chapter on the inversion of control principle and Laravel's own service container.
My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.
It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!