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

# Code e job

> Come eseguire in background operazioni onerose (invio email, elaborazione immagini, ecc.) con code e job di Laravel.

## Cosa sono le code

Nelle applicazioni web puoi avere operazioni che richiedono qualche secondo: invio email, ridimensionamento immagini, chiamate ad API esterne.
Eseguirle sincronamente in una richiesta HTTP costringe l'utente ad aspettare.

Con le code di Laravel esegui questi lavori **in background in modo asincrono**.
La richiesta risponde subito e i worker si occupano del lavoro.

<Info>
  Le code supportano più backend: database, Redis, Amazon SQS, ecc.
  In sviluppo, con il driver `sync` i job vengono eseguiti immediatamente senza coda.
</Info>

```mermaid theme={null}
flowchart LR
    A["Creazione job<br>dispatch()"] --> B["Driver coda<br>(Redis/DB, ecc.)"]
    B --> C["Worker<br>queue:work"]
    C --> D{"Successo?"}
    D -->|"Sì"| E["Completato"]
    D -->|"No"| F{"Retry?"}
    F -->|"Sì"| B
    F -->|"No"| G["Registro fallimenti<br>failed_jobs"]
```

## Configurazione delle code

### config/queue.php

Impostazioni in `config/queue.php`. Cambia driver con `QUEUE_CONNECTION`.

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

### .env

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

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

### Preparazione del driver database

Serve una tabella `jobs`. In Laravel 11+ è già inclusa nella migration.
Altrimenti:

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

### Preparazione del driver Redis

Aggiungi la connessione in `config/database.php` e installa il driver.

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

### SQS Overflow Storage

Amazon SQS ha un limite sulla dimensione dei payload. Per job con payload grandi puoi salvare l'eccesso in un cache store e inviare solo un puntatore.

```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` attivo: payload > **1MB** salvati nel cache store.
* `always` a `true`: salva sempre nel cache store.
* `delete_after_processing` (default `true`): elimina i payload salvati al successo.
* `flush_on_clear` a `true`: `queue:clear` esegue `flush` dello store di overflow. Usa uno store dedicato.

## Creazione della classe job

### make:job

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

Genera `app/Jobs/SendWelcomeEmail.php`.

### Struttura del job

```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;

    public function __construct(
        public User $user,
    ) {}

    public function handle(): void
    {
        Mail::to($this->user->email)->send(new WelcomeMail($this->user));
    }
}
```

`ShouldQueue` indica che va elaborato via coda. Il trait `Queueable` fornisce i metodi necessari.

<Tip>
  Passando un modello Eloquent al costruttore, Laravel serializza solo l'ID e ricarica i dati aggiornati in fase di esecuzione: payload più leggeri.
</Tip>

## Dispatch del job

### 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');
}
```

### Ritardato

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

### dispatchAfterResponse()

Esegui **subito dopo** la risposta HTTP. Funziona anche con `sync`.

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

### Coda specifica

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

### Queue Routing

Instrada per default certi job a una connessione/coda tramite `Queue::route()` nel `boot()` di un ServiceProvider.

```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');
}
```

Puoi indicare interfacce, trait o classi genitore.

Più job insieme:

```php theme={null}
Queue::route([
    ProcessPodcast::class => ['podcasts', 'redis'],
    ProcessVideo::class => 'videos',
]);
```

<Info>
  Il Queue Routing è sovrascrivibile a livello di job con `onQueue()` / `onConnection()`.
</Info>

### Esecuzione sincrona (test/sviluppo)

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

### Dispatch in bulk

Per molti job indipendenti senza tracciamento come nei batch:

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

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

<Info>
  `Bus::bulk()` invia in modo efficiente ma senza le funzionalità di batch (progresso, callback).
</Info>

## Elaborazione dei job

### queue:work

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

Con driver/coda specifici:

```shell theme={null}
php artisan queue:work redis --queue=emails
php artisan queue:work database
```

<Warning>
  `queue:work` resta in esecuzione. Riavvia con `queue:restart` dopo modifiche.
  In produzione usa Supervisor.
</Warning>

## Opzioni del worker

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

| Opzione        | Descrizione                               |
| -------------- | ----------------------------------------- |
| `--tries=N`    | Tentativi max prima del fallimento        |
| `--timeout=N`  | Secondi max per job                       |
| `--sleep=N`    | Attesa quando la coda è vuota (default 3) |
| `--max-jobs=N` | Termina dopo N job                        |
| `--max-time=N` | Termina dopo N secondi                    |
| `--queue=A,B`  | Priorità di code (A prima)                |

### Impostazioni nella classe

```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;
}
```

## Rilascio del job (middleware Release)

Per rimettere il job in coda al soddisfacimento di una condizione.

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

public function middleware(): array
{
    return [
        Release::when($this->order->isPending(), releaseAfter: 60),
    ];
}
```

`Release::unless()` rilascia quando la condizione è `false`.

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

Con closure:

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

<Warning>
  Rilasciare incrementa il contatore dei tentativi. Configura correttamente `#[Tries]` o `$tries`.
</Warning>

## Gestione dei job falliti

### Tabella failed\_jobs

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

### Cleanup al fallimento

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

public function failed(?Throwable $exception): void
{
    // Notifica ad admin, ecc.
}
```

### Interrompere il retry per certe eccezioni

In `withExceptions()` di `bootstrap/app.php`:

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

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

Con closure:

```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>
  Per eccezioni deterministiche (validazione, sottoscrizione scaduta) è efficiente fallire subito.
</Tip>

### Elenco job falliti

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

### Retry

```shell theme={null}
php artisan queue:retry ce7bb17c-cdd8-41f0-a8ec-7b4fef4e5ece
php artisan queue:retry all
```

### Eliminazione

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

## Driver più usati

### database

Semplice, senza middleware aggiuntivi. Salva su `jobs` e i worker fanno polling.

* Pro: setup semplice, riusa RDBMS
* Contro: carico DB, non adatto a grandi volumi

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

### redis

Il più usato in produzione. In memoria, alta throughput.

* Pro: veloce, scalabile
* Contro: richiede server Redis

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

<Tip>
  In produzione considera [Laravel Horizon](https://laravel.com/docs/horizon) per monitorare in tempo reale.
</Tip>

## Esercizio in produzione con Supervisor

In produzione serve un supervisor che riavvii `queue:work` se cade.

```ini theme={null}
[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` per due worker in parallelo.

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

## Esempio pratico: invio email in coda

<Steps>
  <Step title="Crea il job">
    ```shell theme={null}
    php artisan make:job SendOrderConfirmation
    ```
  </Step>

  <Step title="Implementa">
    ```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="Dispatch dal controller">
    ```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', 'Ordine ricevuto.');
    }
    ```
  </Step>

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

## Riepilogo

<AccordionGroup>
  <Accordion title="Quando usare le code">
    * Invio email/SMS
    * Elaborazione immagini/video
    * Chiamate ad API esterne
    * Generazione report o export CSV
    * Invio webhook
  </Accordion>

  <Accordion title="Suggerimenti in sviluppo">
    Con `QUEUE_CONNECTION=sync` i job vengono eseguiti subito senza worker.

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

  <Accordion title="Comandi comuni">
    ```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

- [Laravel Telescope](/it/telescope.md)
- [Concorrenza](/it/concurrency.md)
- [Laravel Horizon](/it/horizon.md)
- [Laravel Cloud — la panoramica completa del PaaS dedicato a Laravel](/it/blog/laravel-cloud.md)
- [Attributi PHP](/it/advanced/php-attributes.md)
