0

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 in jobs table. 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 (uses terminate() 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)