In this is post we’ll be looking at what event-driven programming is and how to get started building event-driven applications in Laravel. We’ll also look at how to decouple various aspects of your application by using event-driven programming.
As a heads up, this article assumes you have prior knowledge of Laravel Events and will not cover the basics. You might want to visit Laravel Events if there is a need to catch up.
What are Event Driven Applications?
Before we dive into what Event Driven Applications are, let’s also take a moment to define what Event Driven Programming is. According to Wikipedia:
Event-driven programming is a programming paradigm in which the flow of the program is determined by events such as user actions (mouse clicks, key presses), sensor outputs, or messages from other programs/threads. Event-driven programming is the dominant paradigm used in graphical user interfaces and other applications (e.g. JavaScript web applications) that are centered on performing certain actions in response to user input.
With the understanding of the definition above, an event-driven application is an application that largely responds to actions generated by the user or the application. The application then executes other code in response to the actions.
What Are Laravel Events?
Simply put, events are actions that occur within an application. Coming from the world of JavaScript, you probably understand events as user actions such as mouse click, mouseover, key press etc. In Laravel, events can be various actions that occur within an application such as email notifications, logging, user sign up, CRUD operations etc. Laravel events provide a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application.
Some of these events are fired automatically by the framework (Laravel) when certain activities occur. For example, when create, save, update or delete operations are performed on an Eloquent model, Laravel will fire created
, saved
, updated
and deleted
events respectively. We can hook into these events and perform other activities within our application. In addition to those events which Laravel automatically fires, you can also fire your own custom events to suit your application. For example, you could fire a userRegistered
event to send a welcome mail to users that sign up on your application.
Firing an event won’t do anything on its own; we have to listen to the event that has been fired and respond accordingly. Laravel events are comprised of two parts: Event Handler and Event Listener. The Event Handler contains the information associated with the event fired. The Event Listener, on the other hand, simply listens for the event instance and responds to it accordingly. Within the Listener is where we’ll implement the event implementation logic. Event classes are typically stored in the app/Events
directory, while their listeners are stored in app/Listeners
.
Why Event-Driven Laravel Applications?
So you have seen what event-driven applications and Laravel events are, and you are wondering why you should build your application using this event-driven approach. Well, let’s take a look at some of its benefits.
Firstly, events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. By decoupling, you don’t pollute your code base with code that doesn’t necessarily fit the domain logic. Secondly, because your application is loosely coupled, you can easily extend the functionality of the application without breaking/rewriting the application or at least some other functionalities of the application.
Practical Implementation
Can we see some code 😎. Imagine we have an application that we want to send welcome emails to newly signed up users and at the same time sign them up for weekly newsletters. Normally, we might have something similar to the snippet below to accomplish that:
// without event-driven approach
public function register(Request $request)
{
// validate input
$this->validate($request->all(), [
'name' => 'required',
'email' => 'required|unique:users',
'password' => 'required|min:6|confirmed',
]);
// create user and persist in database
$user = $this->create($request->all());
// send welcome email
Mail::to($user)->send(new WelcomeToSiteName($user));
// Sign user up for weekly newsletter
Newsletter::subscribe($user->email, [
'FNAME': $user->fname,
'LNAME': $user->lname
], 'SiteName Weekly');
// login newly registered user
$this->guard()->login($user);
return redirect('/home');
}
As you can see, the send welcome mail and newsletter signup are tightly coupled to the register
method. According to the principle of separation of concerns, is the register
method supposed to be concerned with the actual implementations of sending a welcome email or even newsletter signup? Though you might be thinking, well it just sending mail and newsletter signup which are quite small and it’s fine in the register
method. What if in addition to sending mail, you want to also send SMS? You might end up with something like below:
// without event-driven approach
public function register(Request $request)
{
// validate input
// create user and persist in database
// send welcome email
Mail::to($user)->send(new WelcomeToSiteName($user));
// send SMS
Nexmo::message()->send([
'to' => $user->phone_number,
'from' => 'SiteName',
'text' => 'Welcome and thanks for signup on SiteName.'
]);
// Sign user up for weekly newsletter
Newsletter::subscribe($user->email, [
'FNAME': $user->fname,
'LNAME': $user->lname
], 'SiteName Weekly');
// login newly registered user
return redirect('/home');
}
You can see how easily the code base begins to be bloated. But how can we resolve this? Event driven programming to the rescue! So let’s see what the code will look like using this approach to achieve the same functionality as above.
// with event-driven approach
public function register(Request $request)
{
// validate input
$this->validate($request->all(), [
'name' => 'required',
'email' => 'required|unique:users',
'password' => 'required|min:6|confirmed',
]);
// create user and persist in database
$user = $this->create($request->all());
// fire event once user has been created
event(new UserRegistered($user));
// login newly registered user
$this->guard()->login($user);
return redirect('/home');
}
Now once a user is created, the UserRegistered
event will be fired. Recall that we mentioned earlier that firing an event won’t do anything on its own; we need to listen for the UserRegistered
event and carry out necessary actions. Let’s create the UserRegistered
event and the SendWelcomeMail
and SignupForWeeklyNewsletter
listeners to start with:
artisan make:event UserRegistered
artisan make:listener SendWelcomeMail --event=UserRegistered
artisan make:listener SignupForWeeklyNewsletter --event=UserRegistered
Open app/Events/UserRegistered.php
and update the constructor as below:
// app/Events/UserRegistered.php
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
By declaring the $user
variable public
, it will be passed to the listeners and the listeners can in turn do whatever that’s necessary with it. Next, the event listeners will receive the event instance in their handle
method. Within the handle
method, we can perform any actions necessary to respond to the event:
// app/Listeners/SendWelcomeMail.php
public function handle(UserRegistered $event)
{
// send welcome email
Mail::to($event->user)->send(new WelcomeToSiteName($event->user));
}
// app/Listeners/SignupForWeeklyNewsletter.php
public function handle(UserRegistered $event)
{
// Sign user up for weekly newsletter
Newsletter::subscribe($event->user->email, [
'FNAME': $event->user->fname,
'LNAME': $event->user->lname
], 'SiteName Weekly');
}
You can see how we have been able to get loosely coupled code and so kept our register
method code as minimal as possible. Now let’s say we want to send SMS to newly registered users. All we have to do is create a new event listener to listen for when UserRegistered
event is fired:
artisan make:listener SendWelcomeSMS --event=UserRegistered
And place the SMS sending implementation within the handle
method of SendWelcomeSMS
:
// app/Listeners/SendWelcomeSMS.php
public function handle(UserRegistered $event)
{
// send SMS
Nexmo::message()->send([
'to' => $event->user->phone_number,
'from' => 'SiteName',
'text' => 'Welcome and thanks for signup on SiteName.'
]);
}
That’s all. Notice that there was no need to alter or add anything to the register
method.
Conclusion
In this post, we have been able to understand what event-driven programming is, what event-driven applications are and what Laravel events are. We also looked at possible advantages of event-driven applications. But like pretty much every programming concept that has positive effects, it also tends to have downsides. The major downside to event-driven applications is that it can be hard to really understand the flow of the application. Take our implementation above, for example, someone (say a new developer) going through the register
method might not know we are sending a welcome mail to the new user and also signing them to a newsletter.
So I would say you use event driven-approach in your Laravel applications as creative as possible and not overdo it. Generally, I think event-driven Laravel applications are cool 😎. What do you think? Let’s have your thought in the comment below.
This post was originally posted by the author on the Pusher blog.
Top comments (2)
Nice and clean. Thanks. Quick note though... I don't think it would be too much of a problem for devs inheriting the project to quickly follow the flow. They can just glance at the Event Service Provider.
Yeah