Event subscribers — handling many events in one class
Intermediate5 min read·lv-18-005
Concept
Queues allow deferring time-consuming tasks — sending emails, processing images, calling slow external APIs — to background workers. The request returns immediately while the worker processes the job asynchronously.
Queue drivers:
sync: Executes immediately in the same process (no actual queuing). Default in.env. Good for development/testing.database: Jobs stored injobstable. Simple setup, no additional infrastructure. Slower for high throughput.redis: Fast in-memory queue. Requires Redis. Best for most production applications.sqs: Amazon SQS. Managed, durable, scales automatically. Good for AWS environments.beanstalkd: Lightweight job queue daemon.
Queue table: php artisan queue:table && php artisan migrate creates the jobs and failed_jobs tables.
Configuration: config/queue.php. Default connection via QUEUE_CONNECTION env var.
Creating a job: php artisan make:job ProcessOrder. Generates app/Jobs/ProcessOrder.php implementing ShouldQueue.
Dispatching:
ProcessOrder::dispatch($order): Push to queue.ProcessOrder::dispatchSync($order): Execute synchronously (bypasses queue).ProcessOrder::dispatchAfterResponse($order): Process after HTTP response is sent (usesterminate()on the request lifecycle). No worker needed, but still synchronous within the PHP process.
Workers: php artisan queue:work processes jobs. queue:work --queue=emails,default processes specific queues in order.
Code Example
php
<?php
// .env
// QUEUE_CONNECTION=redis (or database, sqs, sync)
// config/queue.php — multiple connections
'connections' => [
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => '{default}',
'retry_after' => 90,
'block_for' => null,
],
],
// Create jobs table (for database driver)
// php artisan queue:table
// php artisan migrate
// Making a job
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\Order;
class ProcessOrder implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(private readonly Order $order) {}
public function handle(): void
{
// This runs in the worker process, not the web request
$this->order->fulfill();
$this->order->update(['status' => 'fulfilled']);
\Illuminate\Support\Facades\Mail::to($this->order->customer)
->send(new \App\Mail\OrderFulfilled($this->order));
}
}
// Dispatching
ProcessOrder::dispatch($order); // to default queue
ProcessOrder::dispatch($order)->onQueue('orders'); // to specific queue
ProcessOrder::dispatch($order)->delay(now()->addMinutes(10)); // delayed
ProcessOrder::dispatch($order)->onConnection('sqs'); // to specific driver
ProcessOrder::dispatchAfterResponse($order); // after response sent
// Running a worker
// php artisan queue:work — process 'default' queue
// php artisan queue:work --queue=emails,default — priority queues
// php artisan queue:work --tries=3 — retry up to 3 times
// php artisan queue:listen — worker that restarts after each job (dev)