Queues & Jobs
Push slow work off the request cycle — email, image processing, API calls — into background workers.
Users should never wait for work that does not need to block their response. Sending a welcome email, resizing an upload, calling a slow third-party API — queue it. The request returns instantly; a worker process picks the job up moments later.
Anatomy of a job
A class implementing ShouldQueue with a handle() method. Its constructor arguments are serialised, so pass IDs or models (Laravel re-fetches models via the SerializesModels trait), not giant payloads or closures.
class SendWelcome implements ShouldQueue
{
use Queueable;
public function __construct(public User $user) {}
public function handle(): void { Mail::to($this->user)->send(new Welcome()); }
}
SendWelcome::dispatch($user);Reliability knobs
$tries/backoff— retry transient failures with delay.$timeout— kill a job that hangs.uniqueId()viaShouldBeUnique— prevent duplicates.- Batches & chains — orchestrate groups of jobs with a completion callback.
Example
<?php
namespace App\Jobs;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\{InteractsWithQueue, SerializesModels};
class ChargeOrder implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 5;
public int $timeout = 30;
public function __construct(public Order $order) {}
// No two charge jobs for the same order run concurrently.
public function uniqueId(): string
{
return 'charge-order-' . $this->order->id;
}
// Exponential-ish backoff between retries (seconds).
public function backoff(): array
{
return [10, 30, 60, 120];
}
public function handle(\App\Contracts\PaymentGateway $gateway): void
{
// Idempotent: bail if already charged (retries are safe).
if ($this->order->isPaid()) {
return;
}
$charge = $gateway->charge($this->order->total_cents, $this->order->token);
$this->order->markPaid($charge->id);
}
public function failed(\Throwable $e): void
{
$this->order->markPaymentFailed($e->getMessage());
}
}
// Dispatch AFTER the surrounding transaction commits:
// ChargeOrder::dispatch($order)->afterCommit()->onQueue('payments');When to use it
- An e-commerce site dispatches a SendOrderConfirmationEmail job to the queue after checkout so the HTTP response is returned immediately without waiting for the mail server.
- A SaaS platform delays an UpgradePlanJob by 10 minutes using ->delay(now()->addMinutes(10)) to allow a grace period before provisioning the upgraded tier.
- A failing third-party API call is retried automatically up to 5 times with $tries = 5 and exponential back-off defined in backoff(), without any manual retry logic.
More examples
Create and dispatch a job
Artisan generates the job class with a handle() stub and ShouldQueue interface already applied, ready for queue dispatch.
php artisan make:job SendOrderConfirmationEmail
# Creates app/Jobs/SendOrderConfirmationEmail.phpDefine a queueable job
Implements ShouldQueue with three retries and exponential back-off; SerializesModels safely serializes the Order model for the queue payload.
class SendOrderConfirmationEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $timeout = 60;
public function __construct(private Order $order) {}
public function handle(Mailer $mailer): void
{
$mailer->to($this->order->user)
->send(new OrderConfirmationMail($this->order));
}
public function backoff(): array
{
return [30, 60, 120]; // seconds between retries
}
}Dispatch with delay and chain
dispatch() queues the job immediately; delay() defers it; bus_chain() ensures the three jobs run in sequence, each dependent on the previous succeeding.
// Dispatch immediately
SendOrderConfirmationEmail::dispatch($order);
// Dispatch after 5 minutes
SendOrderConfirmationEmail::dispatch($order)
->delay(now()->addMinutes(5));
// Chain: run second job only after first succeeds
bus_chain([
new ProcessPayment($order),
new FulfillOrder($order),
new SendOrderConfirmationEmail($order),
])->dispatch();
Discussion