This tutorial is out of date and no longer maintained.
Laravel is undoubtedly a very powerful framework with a lot of batteries included. One of the reasons I love Laravel is the fact that it is built on events.
An event is an action or occurrence recognized by a program that may be handled by the program. Some examples of events in Laravel would be:
The reason we use events is that they allow us to separate application concerns and creates a mechanism for us to hook into actions in our application. When an event is fired, the event does not need to know anything about its implementation, all the event needs to know is that an action is performed, the event is triggered, the event sends some data to a listener or subscriber somewhere that has to deal with it.
To show off how an app would start to look without events, we would do something like the following:
Route::get('signup', function() {
// create the new user
$user = User::create($userData);
// a user is created, do things!
// send the user a confirmation email
// set up that users account
// email them introductory information
// do more stuff
});
We can see how the list could start getting a little long. We can separate this out into an event so that a lot of the logic is separated from the event itself.
Another reason we use events is to speed up application data processing.
NOTE: If you are already familiar with Pub Sub, events should not be a new concept.
Still using the social blogging application, when a user creates a blog post, we might have a checklist of things to do like:
Lining actions like this can slow down your application and events can be used to remove this bottleneck.
Laravel also triggers a lot of system events, for example:
These are just some events fired by Laravel.
To generate an event in Laravel, we simply use good old artisan
- php artisan make:event ActionDone
This creates a class with the name ActionDone
in app/Events
directory, and it should look like this:
<?php namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ActionDone extends Event {
use SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return [];
}
}
Event creation is very simple and requires very little setup.
After an event is triggered, the application needs to know how to deal with the event and for that we need a Listener. To generate a listener in Laravel, we can simply use artisan:
- php artisan make:listener ThingToDoAfterEventWasFired --event="ActionDone"
The command to generate a listener for the event takes in a required --event
flag with the name of the event to listen for. The above command creates a class named ThingToDoAfterEventWasFired
in app/Listeners
, and contains;
<?php namespace App\Listeners;
use App\Events\ActionDone;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ThingToDoAfterEventWasFired {
/**
* Create the event listener.
*
* @return void
*/
public function __construct() { // }
/**
* Handle the event.
*
* @param ActionDone $event
* @return void
*/
public function handle(ActionDone $event)
{
//
}
}
In the event listener class, the handle method is programmatically triggered by Laravel and it type-hints the triggered event’s class.
Before we go on firing events here and there, Laravel’s command bus needs to know about the events and their listeners, and how to handle them.
To register the events, we navigate to app/Providers/EventServiceProvider.php
, we find the protected listen property on the EventServiceProvider
class and:
protected $listen = [
'App\Events\ActionDone' => [
'App\Listeners\ThingToDoAfterEventWasFired',
],
];
That’s all we need to do to let the command bus know about an event and its listeners. Notice that there is an array of listeners, meaning the event can have multiple subscribers.
To have an event fire off multiple listeners, just add to that array:
protected $listen = [
'App\Events\ActionDone' => [
'App\Listeners\ThingToDoAfterEventWasFired',
'App\Listeners\OtherThingToDoAfterEventWasFired',
'App\Listeners\AnotherThingToDoAfterEventWasFired',
],
];
Event subscribers are classes that may subscribe to multiple events from within the class itself, allowing you to define several event handlers within a single class. In the above example, we explicitly name the listeners for an event inside the EventServiceProvider.php
file. We’ll define events and listeners in a new UserEventListener.php
file.
Subscribers should define a subscribe method, which will be passed an event dispatcher instance:
<?php namespace App\Listeners;
class UserEventListener {
/**
* Handle user login events.
*/
public function onUserLogin($event) {}
/**
* Handle user logout events.
*/
public function onUserLogout($event) {}
public function subscribe($events)
{
$events->listen(
'App\Events\UserLoggedIn',
'App\Listeners\UserEventListener@onUserLogin'
);
$events->listen(
'App\Events\UserLoggedOut',
'App\Listeners\UserEventListener@onUserLogout'
);
}
}
To register an event subscriber, go back to the EventServiceProvider
class, and in the subscribe property add the subscriber class:
protected $subscribe = [
'App\Listeners\UserEventListener',
];
Now all the events and listeners created in the UserEventListener
will work.
To fire events, we can use the Event
Facade to fire the event
use Event;
use App\Events\ActionDone;
...
Event::fire(new ActionDone());
or we can use an event
helper method to fire the event
use App\Events\ActionDone;
...
event(new ActionDone());
To pass data to the listeners, we can simply use the event class as a DTO.
Sometimes, we don’t want the events/listeners to hold up our application’s processing. For example, we wouldn’t want a user to have to wait for their new user signup email to get emailed to them while they send the request to create their new account.
We can add event listeners to our application queue and it would be handled by whatever queue driver you specify. This would not hold up our app processing. To do this we simply have the Listener class implement Illuminate\Contracts\Queue\ShouldQueue
.
<?php namespace App\Listeners;
use App\Events\ActionDone;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ThingToDoAfterEventWasFired implements ShouldQueue { /*...*/ }
In many modern web applications, WebSockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client.
To make an event broadcastable, you make the event class implement the Illuminate\Contracts\Queue\ShouldBroadcast
<?php namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ActionDone extends Event implements ShouldBroadcast { /*...*/ }
The ShouldBroadcast
interface requires the event class to implement a broadcastOn
method which returns an array of channels the event should broadcast on.
public function broadcastOn()
{
return ['action-did-occur'];
}
By default, Laravel serializes the event’s public properties and sends it as JSON to the client. If you want more control over what is sent to the client you can add a broadcastWith
method to the event class and return the data to be converted into JSON.
public function broadcastWith()
{
return [
'user' => [
'name' => 'Klark Cent',
'age' => 30,
'planet' => 'Crypton',
'abilities' => 'Bashing'
]
];
}
Currently, Laravel has only two drivers(pusher and socket.io) to help with data consumption on the client. For the scope of this article, we would be using pusher.
Pusher is a WebSocket as a service, you send a request from your application to the service, and pusher broadcasts the message on all clients.
To consume data on the client using pusher simply do this:
var pusher = new Pusher('pusher-key');
var channel = pusher.subscribe('action-did-occur');
channel.bind('App\Events\ActionDone', function(data) {
console.log(data);
});
Note: you need pusher
’s JavaScript SDK on the page:
<script src="//js.pusher.com/2.2/pusher.min.js"></script>
You also need pusher
’s PHP SDK which is available via composer
:
- composer require pusher/pusher-php-server:~2.0
The demo application is a simple shoutbox, users fill in a form with their Twitter handle, email address, and what they would like to shout out.
Configure your .env
file, mine looks like this:
APP_ENV=local
APP_DEBUG=true
APP_KEY=APP_KEY
DB_HOST=localhost
DB_DATABASE=scotchbox
DB_USERNAME=root
DB_PASSWORD=password
PUSHER_KEY=PUSHER_KEY
PUSHER_SECRET=PUSHER_SECRET
PUSHER_APP_ID=PUSHER_APP_ID
For more info on the .env
file and what it does, read the following: Understanding Laravel Environment Variables
Pusher is a great service that makes real-time components of our application effortless. They are a paid service but there is a free sandbox option (20 max connections, 100k messages per day) which should be enough for a small application or just for development. Go ahead and create an account on Pusher to get your credentials.
Let’s get our database ready to hold our shoutouts
. We’re going to need to create the shoutouts
table using a migration and an Eloquent model.
Run the following from the command line at the root of your project:
- php artisan make:migration create_shoutouts_table --create=shoutouts && php artisan make:model Models/Shoutout
The migration created by artisan (database/migrations/create_shoutouts_table.php
) contains:
$table->increments('id');
$table->string('handle');
$table->string('email');
$table->text('content');
$table->timestamps();
The model should have protected fillable
and table
properties
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'shoutouts';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['handle', 'email', 'content'];
Next, we will set up routing and move on to creating our controller. In our app\Http\routes.php
file, we will create a resource route.
Route::resource('shoutouts', 'SiteController');
Now that we have the routes mapped out, let’s create the SiteController
using artisan.
- php artisan make:controller SiteController
This resource route will automatically create some routes for our application including the ones needed for CRUD operations. To see the routes created, just use:
- php artisan route:list
The store
method in the newly created SiteController
will be the place where we handle creating and storing shoutboxes to the database. In our controller store
method let’s add the following:
<?php namespace App\Http\Controllers;
...
use App\Models\Shoutbox;
public function store(Request $request) {
$validator = Validator::make($request->all(), Shoutout::$rules);
/**
* Try validating the request
* If validation failed
* Return the validator's errors with 422 HTTP status code
*/
if ($validator->fails())
{
return response($validator->messages(), 422);
}
$shoutout = Shoutout::create($request->only('email', 'handle', 'content'));
// fire ShoutoutAdded event if shoutout successfully added to database
event(new ShoutoutAdded($shoutout));
return response($shoutout, 201);
}
On the client-side of things (JavaScript), connect to Pusher and listen to the App\Events\ShoutoutAdded
event.
var notifyUser = function (data) {
var data = data.shoutout;
if (! ('Notification' in window)) {
alert('Web Notification is not supported');
return;
}
Notification.requestPermission(function(permission){
var notification = new Notification('@'+ data.handle +' said:', {
body: data.content,
icon: document.getElementById('site_image').content
});
};
var loadPusher = function (){
Pusher.log = function(message) {
if (window.console && window.console.log) {
window.console.log(message);
}
};
var pusher = new Pusher(document.getElementById('pusher_key').content);
var channel = pusher.subscribe('shoutout-added');
channel.bind('App\\Events\\ShoutoutAdded', notifyUser);
};
The client listens for any broadcasted event and uses the Notification API to alert any user currently on the site. All we had to do to fire off our chain of events was use the following line in our controller:
// fire ShoutoutAdded event if shoutout successfully added to the database
event(new ShoutoutAdded($shoutout));
You can see how we could separate out our application logic from the controller and into the event listener. That keeps our application lean and mean.
Hopefully, this helps you to see how easily events can be used and how they can streamline our applications.
Events inform the application that an action occurred. They should not be misused, not every operation requires an event, but if they can move a lot of logic out of your more bloated controllers, then we’ll be better organized.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!