Queues are ways in which we enable our application to listen and act based on predefined events. They allow us to delay tasks that would otherwise interfere with the user experience or our application’s performance.
From the Laravel docs, "Laravel queues provide a unified API across a variety of different queue backends, such as Beanstalk, Amazon SQS, Redis, or even a relational database."
What are we Building?
In this tutorial, we will be creating an application that utilizes Laravel queues to send our users “Happy Birthday” messages on their birthday. We will create a command that fetches all users whose birthdays are today, and sends them to the queue to be processed.
Pre-requisites
- Basic understanding of the Laravel framework
- Composer
- A Twilio Account and Twilio SDK credentials
Setting Up our Application
As our application is Laravel-based, we will create a new Laravel application. If you're not familiar with the Laravel framework, here is a solid guide to help you get started. Create a new application with the laravel new
command and assign the folder name.
$ laravel new birthday-reminder
$ cd birthday-reminder
Next, Update the database-related configurations in your project's .env
file. If you are unfamiliar with dotenv, you can learn more about how this configuration safely protects your credentials from public view.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=birthday_reminder
DB_USERNAME=your_db_username
DB_PASSWORD=your_db_password
This assumes that you already have a database named birthday_reminder
. If you don't, feel free to create it at this point.
Now, open up the user migrations file located at YOUR-APP-NAME/database/migrations/2014_10_12_000000_create_users_table.php
and update the up()
method to this:
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->string('phone')->unique();
$table->date('date_of_birth');
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
This method will generate the users table with the phone
and date_of_birth
columns.
Next we will modify the $fillable
attribute of our User model class to reflect the changes in our users table structure. The User Model can be found in the file app/User.php
.
protected $fillable = [
'name', 'email', 'phone', 'date_of_birth', 'password',
];
Seeding the Database
In real-life, our users would be providing us with the data at sign up or when they update their profile. To keep things simple, we will seed our database with sample data. Let's create the seeder file by running php artisan make:seeder UsersTableSeeder
from our project root and add the following code to the newly created file database/seeds/UsersTableSeeder.php
.
<?php
use Illuminate\Database\Seeder;
use App\User;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$users = [
[
'name' => "Oyewole Precious",
'email' => "sansa@example.com",
'phone' => "+234957000000",
'date_of_birth' => "2000-09-29",
'password' => "super_secret"
],
[
'name' => "Akande Salami",
'email' => "cersei@example.com",
'phone' => "+234956000001",
'date_of_birth' => "1997-10-01",
'password' => "super_secret"
]
];
foreach ($users as $userData) {
$user = new User($userData);
$user->save();
}
}
}
NOTE: To see this work though, use real phone numbers and use the current date as the date_of_birth
, so that our queue can process them instantly.
Next, open up database/seeds/DatabaseSeeder.php
and paste in the following:
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
This will tell Laravel to run our UsersTableSeeder
when we attempt to seed the database. Now, run the seeders with:
$ php artisan migrate
$ php artisan db:seed
Install the Twilio PHP SDK
The Twilio SMS API provides a robust interface for your application to programmatically use Twilio services. We will be using the Twilio PHP SDK to send the SMS. Import it via Composer with the following command.
$ composer require twilio/sdk
Next, add the variables below to your .env
file.
TWILIO_ACCOUNT_SID="ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
TWILIO_AUTH_TOKEN="Your Auth Token"
TWILIO_PHONE_NUMBER="Your Twilio Phone Number in E.164 format (i.e. +13365555555)"
With that setup, let's make our Twilio configuration available globally. Open up config/services.php
and add the following to the services array already present:
'twilio' => [
'account_sid' => env('TWILIO_ACCOUNT_SID'),
'auth_token' => env('TWILIO_AUTH_TOKEN'),
'phone_number' => env('TWILIO_PHONE_NUMBER')
]
Meet Laravel Jobs
In Laravel, queues are represented by Jobs. Generating a new queued job is accomplished by using the Artisan command, similarly to generating controllers and models. Let's create a job:
$ php artisan make:job SendBirthdayMessage
By default, the above command would create a SendBirthdayMessage.php
file in app/Jobs
. The following code will represent our logic to send a birthday message:
<?php
namespace App\Jobs;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\TwilioException;
use Twilio\Rest\Client;
class SendBirthdayMessage implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $user;
private $twilioClient;
/**
* Create a new job instance.
* @param User $user: A single user instance whose birthday is current date.
* @throws ConfigurationException
* @return void
*/
public function __construct(User $user)
{
$this->user = $user;
$sid = config('services.twilio.account_sid');
$token = config('services.twilio.auth_token');
$this->twilioClient = new Client($sid, $token);
}
/**
* Execute the job.
* @throws TwilioException
* @return void
*/
public function handle()
{
$template = "You're finally old enough to know better! Happy Birthday %s";
$body = sprintf($template, $this->user->name);
$message = $this->twilioClient->messages->create(
$this->user->phone,
[
'from' => config('twilio.phone_number'),
'body' => $body
]
);
Log::info($message->sid);
}
}
Now, we need a way to fetch all users whose birthdays are today, so we can dispatch the jobs that would send the message. To do that, we will use an Artisan command. It’s likely that you've used some of the built-in commands already (think php artisan db:seed
, php artisan serve
, etc). Here is some pretty in-depth documentation for it, should you desire to learn more. We’ll now create a custom one with the following command:
$ php artisan make:command BirthdaysReminderCommand
This command generates a BirthdaysReminderCommand
class in app/Console/Commands
. The signature
attribute of the newly created class is what we call from the Artisan CLI, similar to db:seed
and cache:clear
. The description
attribute is simply a basic summary of what our command does. Update both attributes like so:
<?php
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'birthdays:send-wish';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send out a birthday message to users whose birthdays are today.';
Change the handle()
method to match the code below:
<?php
use Carbon\Carbon;
use App\User;
use App\Jobs\SendBirthdayMessage;
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$today = Carbon::now()->format('Y-m-d');
$users = User::where('date_of_birth', '=', $today)->get();
if (!empty($users)) {
foreach ($users as $user) {
SendBirthdayMessage::dispatch($user);
}
}
}
In essence, what we are doing is fetching all the users whose birthdays are today and dispatching the BirthdayReminderJob which would in turn, send the SMS.
Configuring our Queue Driver
As mentioned earlier, Laravel supports different queue implementation such as Database, Redis, Beanstalk, etc. For this post though, we will be using the Database driver. All we need to do is set up the necessary database tables. This can be achieved by running the following commands:
$ php artisan queue:table
$ php artisan migrate
Now that the table is set up, let's start up our queue and listen for dispatched jobs.
$ php artisan queue:work --daemon
Testing
While our queue listener is still running, run the Artisan commands using the same signature we added to the BirthdaysReminderCommand
:
$ php artisan birthdays:send-wish
If works as expected, the SMS should be sent to the matched user(s) and reflected on the Twilio dashboard as well.
Conclusion
We have a better grasp of using queues with the Laravel framework now. If you're deploying to production, you could set up a cron job to execute birthdays:send-wish
every day or explore process monitors such as Supervisor.
In any case, feel free to reach out to me on Twitter @firechael if you have questions or suggestions.