How to use queues in Laravel

Table of Contents

Introduction

While working on a Laravel project I needed to implement a solution where an e-mail can be sent when a user fills in a contact form. As e-mail isn’t a quick process it’s best practice to handle the sending of emails in the background rather than a browser request.

This is where queues come into play where a request will trigger a job that will be queued and run in a separate process so the user doesn’t have to wait long for the contact form to finish submitting.

Setup (Database queues) {#setup-(database-queues)}

This setup uses a new Laravel 11 project so settings will vary, also SQLite is used by default so the data can be accessed from the database.sqlite inside of the database folder.

Firstly open the config/queue.php file to check the configuration data for queues.

config.png

The queues will be set to the database connection by default, this will be the option used for this guide and other options such as SQS or Redis will be covered at a later time.

Open up the database.sqlite file and check the tables to ensure the jobs and failed_jobs tables are set up.

db.png

If the jobs table isn’t added then run the following commands.

php artisan make:queue-table && php artisan migrate

Do the same for if the failed_jobs table isn’t available.

php artisan make:queue-table && php artisan migrate

Queue code example

This will be a code example showing how to use a database queue.

Blade view

Create the blade view template that’s required for emails in Laravel.

php artisan make:view mail.test-email

Open the test-email.blade.php file inside of the resources/views/mail directory and add the following content.

<div>
    <div>This is a test e-mail message.</div>
</div>

Mailable

Create the Mailable object required for the Mail facade with the command below.

php artisan make:mail TestEmail

Open the TestEmail.php file inside of the app/Mail directory and update the envelope and content methods with the following content.

/**
 * Get the message envelope.
 */
public function envelope(): Envelope
{
    return new Envelope(
        subject: 'Test Email',
    );
}

/**
 * Get the message content definition.
 */
public function content(): Content
{
    return new Content(
        view: 'mail.test-email',
    );
}

Job

When working with queues a Job class is what’s added to a queue and ran when the queue is ready to process code.

Use PHP artisan to create a new Job class.

php artisan make:job SendEmailJob

Open the SendEmailJob.php file inside of the app/Jobs directory and replace the default code with the following code.

This job will be used to send a test email.

<?php

namespace App\Jobs;

use App\Mail\TestEmail;
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\Mail;

class SendEmailJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public function __construct()
    {
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $toAddress = '[email protected]';
        Mail::to($toAddress)->send(new TestEmail());
    }
}

Command

To add the job to a queue a Command will be used, typically a web request would be used to add jobs to queues however to test this out it’s easier to just add it to a command.

Run PHP artisan to add the new job.

php artisan make:command SendMessageCommand

Replace the code inside of the job with the following.

<?php

namespace App\Console\Commands;

use App\Jobs\SendEmailJob;
use Illuminate\Console\Command;

class SendMessageCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:send-email';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Add a job to the queue to send a test email';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        SendEmailJob::dispatch();
    }
}

Add job to the queue

Run the command which will call the dispatch method for the SendEmailJob job, this will add the job to the jobs table in the database.

php artisan app:send-email

If you go to the database through a database client and you can see the database row added to the jobs table.

db-job.png

Process jobs in the queue

Run the following command to start the processing of the queue, typically on a server this command will be run at all times so the queue is always processing jobs as they get added to the queue.

php artisan queue:work

After running the queue work command you will see output for the job that was previously added.

INFO Processing jobs from the [default] queue.

2024-07-12 21:53:54 App\Jobs\SendEmailJob......................................................…. RUNNING
2024-07-12 21:53:54 App\Jobs\SendEmailJob.................................................. 31.55ms DONE

By default the mail configuration is set to log meaning that every time an email is sent the content of the email is sent to the Laravel log file.

To check the output open the laravel.log file inside of the storage/logs directory.

[2024-07-12 21:53:54] local.DEBUG: From: Laravel [email protected]
To: [email protected]
Subject: Test Email
MIME-Version: 1.0
Date: Fri, 12 Jul 2024 21:53:54 +0000
Message-ID: [email protected]
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

This is a test e-mail message.

Conclusion

After going through this guide you will be in a position to implement queues in a Laravel project. As mentioned there are alternative queue methods that can be used such as Amazon SQS and Redis however those two do require additional setup and therefore will take longer to set up, however, either of those two methods would be a much better fit for large applications that are run in production servers.

What isn’t demonstrated in this guide is that whenever a job fails then a new database row will be added in the failed_jobs database table, which is great as we want to retain a record of the data that failed to process in the queue.