Laravel — Event & Queue

Syed Sirajul Islam Anik
9 min readNov 25, 2017

--

Collected From: http://blog.legacyteam.info/wp-content/uploads/2014/10/laravel-logo-white.png

I have been working with Laravel for more than 3 years and I never tried using Queue or Events. 3 or 4 months ago, I tried the Events. Before that, I had no urge to try those things. When I was working on a project, our CTO — Hasin Haider said to fire events for every reasonable database update. And a week ago, I was working on my own. It was a heavy task to do. It’s irrelevant to let user know what I am doing to process his data. Doing heavy tasks while user is waiting for the response will not make him pleasant. What he wants is actually the response that you’ve got for him. He wants your acknowledgement. So, I had to learn about jobs/queue.

What is an Event?

Well, what do you generally think of an event? The definition is here.

A thing that happens or takes place, especially one of importance.

— Google Translator

Exactly, When you try to do something in the database, suppose an user has registered or has logged in or has updated his profile or paid some money to someone else’s account. All of these things are occurrences. And for all of these occurrences, you may want to do something. Maybe you want to notify him by sending him an email. May be some other tasks. That depends on what your feature list describes.

What about Queue?

Everything has its definition, so does queue.

A line or sequence of people or vehicles awaiting their turn to be attended to or to proceed. — Google Translator

When you need to do a heavy, time consuming task to complete but the requesting user wants your response, the place where you put your task is a queue. Time consuming tasks can be like sending an email, reading data from a large csv, processing his uploaded large video files or anything that can take more than a few milliseconds.

Event is NOT Queue

When I started working with the event and queue, I f*cked up. What the hell is event or queue? When and why? Then I read some articles, some of the SO answers. As I mentioned earlier about the event and queue, there is now no reason you will mix up. For understanding queues & events think of a scenario.

  1. You and your Girlfriend went to a restaurant.
  2. You raised your hand for a waiter.
  3. Waiter came in. You told him to serve a plate of rice, two fish fries, beef & a water bottle.
  4. Waiter said, “We don’t serve beef.”
  5. You said, “Ok instead serve two chickens.”
  6. You said him privately that it’s our anniversary. Play some nice music.
  7. Waiter took your order. He told their musicians to play a nice song.
  8. Went to the kitchen, told the chef to cook the food for you.
  9. Musicians comes to your table. Starts playing nice music.
  10. Immediately serves the water bottle with glasses.
  11. Chef is done making the food. Notifies the waiter.
  12. Waiter serves your food.

The interpretation of these can be given from a web engineer’s perspective like,

  1. A request is sent to Laravel application.
  2. Application boots up, passed to controller.
  3. Tries to validate the request data.
  4. Invalidates the request with 422 status code.
  5. User again sends the request. And got accepted.
  6. Checks some metadata (or whatever).
  7. Broadcasts an event to musicians.
  8. Puts a job on queue.
  9. Listeners act accordingly.
  10. Returns the Response.
  11. Job processing done in the background. Notifies server.
  12. Server notifies you with push or updates database to show you on next request.

Now, think the waiter as a controller. When you request with beef and he refused with “No, don’t serve beef.” is actually the validator. For simplicity, just assume you’re validating inside the controller. Now, when you again place an order, he takes your order. As you also mentioned that it’s your anniversary he notifies (broadcasts an event) the musicians to play nice songs. Listening to that broadcast, Musicians play accordingly. Waiter then puts a job on queue by telling the chef to cook the food. He serves you the water means you’re responded by the server with the minimal response.

How to fire and handle an event?

Events are synchronous (can be queued too [Edit: Yue Dai pointed out, It’s the listeners. For simplicity, you probably can assume it’s the events.]). When you fire an event, the listeners to that event are executed and when the event is done executing the calls returns to the callee where the event was fired. Let’s have an example. Before that, if you’re working with Laravel, then EventServiceProvider is already registered. But if you’re working with Lumen, then you’ll have to register the EventServiceProvider which is disabled by default. Just uncomment the line and you’re good to go.

If you’re working with Laravel, php artisan make:event MusicEvent command will create a file inside the app/Events directory. If you open the MusicEvent.php file, you’ll see there are two methods. One is __construct() another one is broadcastOn(). Construct is the constructor method. If you want to pass any value to the event, you’ll have to pass through the constructor of the class. For now, you can ignore the broadcastOn method.

If you’re working with Lumen, then you’ll have to make a copy of the ExampleEvent.php and rename it. Rename the class too. That’s it. For lumen, there is not artisan command available for creating events or listeners.

So far we have created an event class. We need to create a listener class for this. Let’s create one. If you’re working with Laravel then like before php artisan make:listener MusicEventListener command will create a listener inside the app/Listeners directory. If you open the MusicEventListener.php file, you’ll also see that there are two methods. One of them is a constructor and the other one is handle. The constructor can type-hint any dependencies and those type-hints will be resolved via Service Container. And the handle method is responsible for handling the event’s logic.

If you’re working with Lumen, like before just copy the ExampleListener.php and rename the file and class. That’s it.

For both the Laravel and the Lumen, the handle method will handle the logic of your event. Handle method has one argument $event which is actually the MusicEvent for our example. That means, the broadcaster’s handle is fed with the Event for which the broadcaster was called.

When you’re done with the Event and the Listener, then you’ll have to register the event and bind the listener for that event. To do so, open the app/Providers/EventServiceProvider and in the $listen array, add your Event as key and the listener as value in that array.

// ... more codes
protected $listen = [
App\Events\MusicEvent::class => [
App\Listeners\MusicEventListener::class
],
];
// ... more codes

Let’s try some coding here.

app/Http/Controllers/AuthController.php is like below

<?php 
namespace App\Http\Controllers;
use App\Events\UserRegistrationSuccessful;class AuthController extends Controller {

public function registration(){
// user is registered to the database,
// $user is the Eloquent object.
event(new UserRegistrationSuccessful($user));
// event() is a helper function
// return the response
return response()->json(['message' => 'registered'], 201);
}
}

app/Events/UserRegistrationSuccessful — Event can be like this

<?php
namespace App\Events;
class UserRegistrationSuccessful {
public $user;
public function __construct(User $user){
// this $user is passed from the controller
$this->user = $user;
}
// ... more code
}

app\Listeners\UserRegistrationSuccessfulListener — Can be like this.

<?php
namespace App\Listeners;
class UserRegistrationSuccessfulListener {
public function __construct () {
// .. if you need type-hint/DI
}
public function handle ($event) {
// $event instance of App\Event\UserRegistrationSuccessful
// do something here
// and something more.
}
}

That’s it. It is all about the events.

Now, what about Queue?

For events, I have said it’s synchronous. For queue, it can be synchronous or it can be handled later. Laravel/Lumen supports a few drivers to process queue. It’s mentioned in the app/config/queue.php. The sync job means that the queue is processed simultaneously. Which is actually what you had done before knowing queue in laravel. Like, you’re inserting a lots of data in your database from a CSV file and the user is waiting. Or you’re trying to send an email to the client. These are time consuming tasks. If you say that the queue will be processed via sync then it’s actually same as completing from the controller. On the other hand, you can process it via database, beanstalkd, redis and more on.

For this example, we’ll use the database. Firstly, let us setup things to do so. If you’re running an application that interacts with the database then run the following command, php artisan queue:table . This will generate a migration in your database/migrations directory. And run php artisan queue:failed-table, which will create another migration in your database/migrations directory. You don’t need to touch those files. Just run php artisan migrate and it will migrate two tables into your database. Now, edit your .env file and change the QUEUE_DRIVER to database from sync so that Laravel uses database for processing queues. That’s it. We’re good to use Queue.

If you’re using Laravel, then running php artisan make:job MyJob will create a job inside app\Jobs directory. Or if you’re using Lumen, then just copy the app\Jobs\ExampleJob and rename the file and rename the class.

If you open that class, you’ll see two methods. One is __construct which is the constructor. You can pass whatever you want to let you job know it has to deal with. Just add those things in your constructor. And the handle method is responsible for handling the logic of the job. When the job will be executed what it should do is defined in the handle method. Like you want to send an email to the client or you want to read from the csv should be written in the handle method. You can fire a job from anywhere in your code. You can fire the job from event or from controller. It will be stored in the the defined driver (for us it is database).

Let us see something from our code. We will fire the job from the UserRegistrationSuccessful event handler. As the handle method handles the event’s logic, we will fire from the handle method. It doesn’t make sense to fire from the listener’s constructor tho.

app\Listeners\UserRegistrationSuccessfulListener class.

<?php
namespace App\Listeners;
use App\Jobs\SendRegistrationEmail;class UserRegistrationSuccessfulListener {
public function __construct () {
// .. if you need type-hint/DI
}
public function handle ($event) {
// $event instance of App\Event\UserRegistrationSuccessful
// do something here
// and something more.
// dispatch is a helper method
dispatch(new SendRegistrationEmail($event->user));
// we stored the user as public in our EVENT class
}
}

And yes, we need to create a job named SendRegistrationEmail inside the app\Jobs directory.

App\Jobs\SendRegistrationEmail class

<?php
namespace App\Jobs;
// .. more code
class SendRegistrationEmail implements ShouldQueue {
private $user;
public function __construct (User $user) {
// ..
$this->user = $user;
}
public function handle (){
// .. send email to client from here
}
}

When the jobs will be processed, you’ll get the $user in your handle method automatically, as you did pass it to the constructor. As the event’s __construct can have Type-hints, in jobs the handle method can also have type-hints. These are resolved via service container. Now, if you check your database’s jobs table, you’ll see that there is an entry. To process the job, from your terminal run php artisan queue:work and in your terminal you’ll be able to see something like the following,

Processing  App\Jobs\SendRegistrationEmail
Processed App\Jobs\SendRegistrationEmail

while it is processing and processed the job. After successfully processing the job, it is deleted from the table.

Todos for a job:

  1. If you’re not doing sync jobs, then you’ll have to start a worker that will continuously process the job.
  2. If you start the queue:work if you change something in your Job, will not reflect untill you restart the queue:work
  3. queue:restart sends a signal to stop the execution, if you’re working with a supervisor that will automatically start a new one when the queue stopped. If not then you’ll have to do it manually.

If you’ve go the queues so far, you’ll come to understand the other parts easily. Just look at the official documentation. There are lot of other things you can do with the queue. For keeping the article short, I don’t want to elaborate more.

Can events be delayed like queues?

In short, YES. They can be delayed. It’s given in here in the official documentation.

This is it for now. Hope you at least have got the idea of events and queues. The main purpose of the article was to let you know what it is and how it should be done. I didn’t want to let you deep dive into it. If you just come to know how this works, you’ll be able to know what things can be done with it easily.

And if you find any kind of misinformation here, please feel free to let me know and I will update it.

Happy coding…

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote