> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Queues et jobs

> Exécutez des traitements lourds (mail, images…) en asynchrone avec les queues et jobs de Laravel.

## Qu'est-ce qu'une queue

Dans une application web, l'envoi de mails, le redimensionnement d'images ou les appels d'API externes peuvent prendre plusieurs secondes.
Traiter cela de manière synchrone dans une requête HTTP fait attendre l'utilisateur inutilement.

Les queues Laravel exécutent ces tâches **en arrière-plan** : la requête retourne immédiatement une réponse, et un worker traite le job à part.

<Info>
  Backends supportés : base de données, Redis, Amazon SQS…
  En dev, le driver `sync` exécute les jobs immédiatement, sans queue.
</Info>

```mermaid theme={null}
flowchart LR
    A["Création du job<br>dispatch()"] --> B["Driver de queue<br>(Redis/DB…)"]
    B --> C["Worker<br>queue:work"]
    C --> D{"Succès ?"}
    D -->|"Oui"| E["Terminé"]
    D -->|"Non"| F{"Retry possible ?"}
    F -->|"Oui"| B
    F -->|"Non"| G["Échec<br>failed_jobs"]
```

## Configuration

### `config/queue.php`

```php theme={null}
// config/queue.php
'default' => env('QUEUE_CONNECTION', 'database'),
```

### `.env`

```ini theme={null}
# Sélection du driver
QUEUE_CONNECTION=database

# Redis
# QUEUE_CONNECTION=redis
# REDIS_HOST=127.0.0.1
# REDIS_PORT=6379
```

### Préparer le driver database

`database` nécessite une table `jobs`. Sous Laravel 11+, la migration est incluse ; sinon :

```shell theme={null}
php artisan make:queue-table
php artisan migrate
```

### Préparer le driver Redis

Configurez la connexion Redis dans `config/database.php` puis installez :

```shell theme={null}
composer require predis/predis
```

### Overflow storage SQS

Amazon SQS limite la taille de payload. Pour de gros payloads, redirigez l'excédent vers un cache et n'envoyez qu'un pointeur.

```php theme={null}
'sqs' => [
    // ...
    'overflow' => [
        'enabled' => env('SQS_OVERFLOW_ENABLED', false),
        'store' => env('SQS_OVERFLOW_STORE'),
        'always' => false,
        'delete_after_processing' => true,
        'flush_on_clear' => env('SQS_OVERFLOW_FLUSH_ON_CLEAR', false),
    ],
],
```

* `enabled` : payloads ≥ 1 Mo → cache.
* `always: true` : toujours passer par le cache.
* `delete_after_processing` : nettoie le cache après succès (`true` par défaut).
* `flush_on_clear: true` : `queue:clear` purge le cache d'overflow. À combiner avec un store dédié pour ne pas vider le cache général.

## Créer une classe de job

### Commande `make:job`

```shell theme={null}
php artisan make:job SendWelcomeEmail
```

Le fichier `app/Jobs/SendWelcomeEmail.php` est créé.

### Structure

```php theme={null}
<?php

namespace App\Jobs;

use App\Models\User;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    /**
     * Instance du job.
     */
    public function __construct(
        public User $user,
    ) {}

    /**
     * Exécution du job.
     */
    public function handle(): void
    {
        Mail::to($this->user->email)->send(new WelcomeMail($this->user));
    }
}
```

`ShouldQueue` indique un traitement asynchrone. Le trait `Queueable` fournit les méthodes nécessaires.

<Tip>
  Passer un modèle Eloquent au constructeur sérialise seulement son ID. Au moment de l'exécution, le modèle est récupéré à jour : payload allégé.
</Tip>

## Dispatch

### `dispatch()`

```php theme={null}
use App\Jobs\SendWelcomeEmail;

public function register(Request $request): RedirectResponse
{
    $user = User::create($request->validated());

    SendWelcomeEmail::dispatch($user);

    return redirect('/dashboard');
}
```

### Dispatch différé

```php theme={null}
// 5 min plus tard
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));
```

### `dispatchAfterResponse()`

Exécute le job **après** l'envoi de la réponse HTTP. Fonctionne aussi en `sync` — idéal pour du léger sans worker.

```php theme={null}
SendWelcomeEmail::dispatchAfterResponse($user);
```

### Vers une queue précise

```php theme={null}
SendWelcomeEmail::dispatch($user)->onQueue('emails');
```

### Routing de queue

Pour rediriger par défaut certaines classes vers une connexion/queue précise, utilisez `Queue::route()` dans `boot()`.

```php theme={null}
use App\Concerns\RequiresVideo;
use App\Jobs\ProcessPodcast;
use App\Jobs\ProcessVideo;
use Illuminate\Support\Facades\Queue;

public function boot(): void
{
    Queue::route(ProcessPodcast::class, connection: 'redis', queue: 'podcasts');
    Queue::route(RequiresVideo::class, queue: 'video');
}
```

Ciblez interface, trait, classe parente… tout ce qui les implémente/étend est routé.

Plusieurs routes en une :

```php theme={null}
Queue::route([
    ProcessPodcast::class => ['podcasts', 'redis'], // queue et connexion
    ProcessVideo::class => 'videos',                // queue seule (connexion par défaut)
]);
```

<Info>
  Un `onQueue()` / `onConnection()` explicite sur le job prime sur le routing.
</Info>

### Sync (dev/tests)

`dispatchSync()` exécute immédiatement, sans queue.

```php theme={null}
SendWelcomeEmail::dispatchSync($user);
```

### Bulk dispatch

Pour envoyer beaucoup de jobs indépendants d'un coup sans tracking : `Bus::bulk()`.

```php theme={null}
use App\Jobs\ProcessUser;
use Illuminate\Support\Facades\Bus;

Bus::bulk(
    $users->map(fn ($user) => new ProcessUser($user))
);
```

<Info>
  `Bus::bulk()` groupe les jobs par connexion/queue et les envoie par lot. Contrairement à `Bus::batch()`, pas de suivi ni de callback.
</Info>

## Traiter les jobs

### `queue:work`

```shell theme={null}
php artisan queue:work
```

Ciblez driver et file :

```shell theme={null}
# File emails sur Redis
php artisan queue:work redis --queue=emails

# Driver database
php artisan queue:work database
```

<Warning>
  `queue:work` reste actif. Après un changement de code, `queue:restart` recharge les workers.
  En production, un superviseur (Supervisor…) est recommandé.
</Warning>

## Options de supervision

```shell theme={null}
php artisan queue:work --tries=3 --timeout=60 --sleep=3
```

| Option         | Description                                      |
| -------------- | ------------------------------------------------ |
| `--tries=N`    | Tentatives max ; au-delà, échec                  |
| `--timeout=N`  | Durée max d'un job (secondes)                    |
| `--sleep=N`    | Attente entre polls quand la file est vide (3 s) |
| `--max-jobs=N` | Termine après N jobs                             |
| `--max-time=N` | Termine après N secondes                         |
| `--queue=A,B`  | Priorise A avant B                               |

### Retry paramétré côté job

Souvent plus lisible que la CLI.

```php theme={null}
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;

#[Tries(3)]
#[Timeout(60)]
class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    // ...
}
```

## Libération de job (middleware Release)

Pour renvoyer le job dans la queue sans l'exécuter, sous condition, utilisez le middleware `Release`.

```php theme={null}
use Illuminate\Queue\Middleware\Release;

/**
 * Middlewares que le job traverse.
 */
public function middleware(): array
{
    return [
        // Si $condition == true, libère après 60 s
        Release::when($this->order->isPending(), releaseAfter: 60),
    ];
}
```

`Release::unless()` libère si la condition est `false`.

```php theme={null}
return [
    Release::unless($this->order->isPaid(), releaseAfter: 60),
];
```

Une closure pour des cas plus complexes :

```php theme={null}
return [
    Release::when(function (): bool {
        return ! $this->order->isPaid();
    }, releaseAfter: 60),
];
```

<Warning>
  Chaque libération incrémente le compteur d'essais. Réglez `#[Tries]` ou `$tries` en conséquence.
</Warning>

## Jobs échoués

### Table `failed_jobs`

Après épuisement des tentatives, le job est stocké dans `failed_jobs`.

```shell theme={null}
php artisan make:queue-failed-table
php artisan migrate
```

### Cleanup à l'échec

Définissez `failed()` sur le job.

```php theme={null}
use Throwable;

public function failed(?Throwable $exception): void
{
    // Notifier un admin Slack, etc.
    // Notification::route('slack', config('app.slack_webhook'))
    //     ->notify(new JobFailedNotification($this, $exception));
}
```

### Arrêter les retries par type d'exception

Utilisez `dontRetry` dans `withExceptions()` (fichier `bootstrap/app.php`).

```php theme={null}
use App\Exceptions\InvalidPodcastSourceException;
use Illuminate\Foundation\Configuration\Exceptions;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontRetry([
        InvalidPodcastSourceException::class,
    ]);
})
```

Avec une closure pour un contrôle fin :

```php theme={null}
use App\Exceptions\PodcastProcessingException;
use Illuminate\Foundation\Configuration\Exceptions;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontRetryWhen(function (PodcastProcessingException $e) {
        return $e->reason() === 'Subscription expired';
    });
})
```

<Tip>
  Idéal pour des cas où retenter ne changera rien (validation, échec de paiement, abonnement expiré…).
</Tip>

### Lister les échecs

```shell theme={null}
php artisan queue:failed
```

### Retry

```shell theme={null}
# Un job précis
php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

# Tous
php artisan queue:retry all
```

### Suppression

```shell theme={null}
# Un job
php artisan queue:forget ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece

# Tous
php artisan queue:flush
```

## Drivers courants

### `database`

Simple à mettre en place, utilise la RDBMS existante.

* **Avantages** : setup rapide, sans dépendance
* **Inconvénients** : lourd pour de gros volumes

```ini theme={null}
QUEUE_CONNECTION=database
```

### `redis`

Driver rapide, très utilisé en production. Débit supérieur.

* **Avantages** : rapide, scalable
* **Inconvénients** : nécessite Redis

```ini theme={null}
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
```

<Tip>
  En production, [Laravel Horizon](https://laravel.com/docs/horizon) offre un beau dashboard pour surveiller les jobs Redis en temps réel.
</Tip>

## Supervisor en production

Sous Linux, faites tourner `queue:work` en continu avec Supervisor.

```ini theme={null}
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/your-app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/your-app/storage/logs/worker.log
stopwaitsecs=3600
```

`numprocs=2` démarre 2 workers en parallèle. Rechargez Supervisor :

```shell theme={null}
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
```

## Exemple : envoi d'e-mail en queue

<Steps>
  <Step title="Créer le job">
    ```shell theme={null}
    php artisan make:job SendOrderConfirmation
    ```
  </Step>

  <Step title="Implémenter">
    ```php theme={null}
    <?php

    namespace App\Jobs;

    use App\Models\Order;
    use App\Mail\OrderConfirmed;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Queue\Queueable;
    use Illuminate\Queue\Attributes\Tries;
    use Illuminate\Queue\Attributes\Timeout;
    use Illuminate\Support\Facades\Mail;

    #[Tries(3)]
    #[Timeout(30)]
    class SendOrderConfirmation implements ShouldQueue
    {
        use Queueable;

        public function __construct(
            public Order $order,
        ) {}

        public function handle(): void
        {
            Mail::to($this->order->user->email)
                ->send(new OrderConfirmed($this->order));
        }
    }
    ```
  </Step>

  <Step title="Dispatcher depuis un contrôleur">
    ```php theme={null}
    use App\Jobs\SendOrderConfirmation;

    public function store(Request $request): RedirectResponse
    {
        $order = Order::create($request->validated());

        SendOrderConfirmation::dispatch($order);

        return redirect()->route('orders.show', $order)
            ->with('success', 'Commande enregistrée.');
    }
    ```
  </Step>

  <Step title="Démarrer un worker">
    ```shell theme={null}
    php artisan queue:work --tries=3 --timeout=30
    ```
  </Step>
</Steps>

## Récapitulatif

<AccordionGroup>
  <Accordion title="Quand utiliser une queue">
    * Envoi de mails / SMS
    * Redimensionnement d'images / vidéos
    * Appels d'API externes
    * Génération de rapports / exports CSV
    * Envoi de webhooks
  </Accordion>

  <Accordion title="Astuce en dev">
    `QUEUE_CONNECTION=sync` dans `.env` exécute immédiatement — pas besoin de worker pour tester.

    ```ini theme={null}
    QUEUE_CONNECTION=sync
    ```
  </Accordion>

  <Accordion title="Commandes fréquentes">
    ```shell theme={null}
    php artisan queue:work

    php artisan queue:restart

    php artisan queue:failed

    php artisan queue:retry all

    php artisan queue:flush
    ```
  </Accordion>
</AccordionGroup>


## Related topics

- [Planification de tâches](/fr/scheduling.md)
- [Techniques pratiques de Laravel Telescope](/fr/blog/telescope-introduction.md)
- [Context](/fr/context.md)
- [Introduction à Laravel Nightwatch](/fr/blog/nightwatch-introduction.md)
- [Mises à jour Laravel — avril 2026](/fr/blog/changelog/202604.md)
