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

# Task scheduling

> Come gestire elegantemente task ricorrenti con lo scheduler di Laravel.

## Cos'è il task scheduling

Tradizionalmente devi scrivere una voce cron per ciascun task ricorrente sul server.
La definizione vive fuori dal codice sorgente, non è versionata e ogni modifica richiede un SSH.

Con lo scheduler di Laravel **definisci lo schedule fluentemente nell'applicazione**.
Sul server basta una sola voce cron e lo schedule è versionato con il codice.

Il posto standard è `routes/console.php`.

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

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schedule;

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
```

<Info>
  Con `schedule:list` vedi task e prossime esecuzioni.

  ```shell theme={null}
  php artisan schedule:list
  ```
</Info>

### Flusso dello scheduler

```mermaid theme={null}
flowchart TD
    A["* * * * * artisan schedule:run<br>(cron ogni minuto)"] --> B["Console Kernel"]
    B --> C["Chiamata a schedule()"]
    C --> D["Valuta i task registrati"]
    D --> E{"L'espressione cron<br>combacia con l'ora corrente?"}
    E -->|"No"| F["Skip"]
    E -->|"Sì"| G{"Condizioni when/skip/<br>environments?"}
    G -->|"Non soddisfatte"| F
    G -->|"OK"| H{"Manutenzione?"}
    H -->|"No"| I["Esegue il task<br>(hook before → body → after)"]
    H -->|"Sì"| J{"evenInMaintenanceMode()?"}
    J -->|"Sì"| I
    J -->|"No"| F
```

## Definizione dello schedule

Definisci in `routes/console.php`. In alternativa in `withSchedule` di `bootstrap/app.php`.

```php theme={null}
use Illuminate\Console\Scheduling\Schedule;

->withSchedule(function (Schedule $schedule) {
    $schedule->call(new DeleteRecentUsers)->daily();
})
```

## Tipi di task pianificabili

### Comandi Artisan

Con `command`.

```php theme={null}
use App\Console\Commands\SendEmailsCommand;
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send Taylor --force')->daily();

Schedule::command(SendEmailsCommand::class, ['Taylor', '--force'])->daily();
```

I comandi definiti tramite closure possono concatenare direttamente lo scheduling.

```php theme={null}
Artisan::command('delete:recent-users', function () {
    DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();
```

### Job in coda

```php theme={null}
use App\Jobs\Heartbeat;
use Illuminate\Support\Facades\Schedule;

Schedule::job(new Heartbeat)->everyFiveMinutes();
```

Coda e connessione:

```php theme={null}
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
```

### Comandi shell

```php theme={null}
Schedule::exec('node /home/forge/script.js')->daily();
```

### Closure

```php theme={null}
Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
```

Oggetti invocabili:

```php theme={null}
Schedule::call(new DeleteRecentUsers)->daily();
```

## Frequenza

| Metodo                      | Descrizione                  |
| --------------------------- | ---------------------------- |
| `->everySecond()`           | Ogni secondo                 |
| `->everyMinute()`           | Ogni minuto                  |
| `->everyFiveMinutes()`      | Ogni 5 minuti                |
| `->everyFifteenMinutes()`   | Ogni 15 minuti               |
| `->everyThirtyMinutes()`    | Ogni 30 minuti               |
| `->hourly()`                | Ogni ora                     |
| `->hourlyAt(17)`            | Ogni ora al minuto 17        |
| `->daily()`                 | Ogni giorno a mezzanotte     |
| `->dailyAt('13:00')`        | Ogni giorno alle 13          |
| `->twiceDaily(1, 13)`       | Ogni giorno alle 1 e alle 13 |
| `->weekly()`                | Ogni domenica mezzanotte     |
| `->weeklyOn(1, '8:00')`     | Ogni lunedì alle 8           |
| `->monthly()`               | 1° del mese mezzanotte       |
| `->monthlyOn(4, '15:00')`   | Il 4 di ogni mese alle 15    |
| `->quarterly()`             | Inizio trimestre mezzanotte  |
| `->yearly()`                | 1° gennaio mezzanotte        |
| `->timezone('Europe/Rome')` | Timezone                     |

### Espressione cron

```php theme={null}
Schedule::command('emails:send')->cron('0 9 * * *');
```

### Combinazione frequenza + giorno

```php theme={null}
Schedule::call(function () {
    // ...
})->weekly()->mondays()->at('13:00');

Schedule::command('foo')
    ->weekdays()
    ->hourly()
    ->timezone('Europe/Rome')
    ->between('8:00', '17:00');
```

### Timezone

```php theme={null}
Schedule::command('report:generate')
    ->timezone('Europe/Rome')
    ->at('9:00');
```

Globale in `config/app.php`:

```php theme={null}
'schedule_timezone' => 'Europe/Rome',
```

<Warning>
  Con l'ora legale i task potrebbero eseguirsi due volte o non eseguirsi affatto. Consigliato UTC quando possibile.
</Warning>

## Vincoli condizionali

### when / skip

```php theme={null}
Schedule::command('emails:send')->daily()->when(function () {
    return true;
});

Schedule::command('emails:send')->daily()->skip(function () {
    return true;
});
```

### environments

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->environments(['staging', 'production']);
```

### Orario

```php theme={null}
Schedule::command('emails:send')
    ->hourly()
    ->between('7:00', '22:00');

Schedule::command('emails:send')
    ->hourly()
    ->unlessBetween('23:00', '4:00');
```

### Giorno

| Metodo                        | Descrizione                              |
| ----------------------------- | ---------------------------------------- |
| `->weekdays()`                | Feriali                                  |
| `->weekends()`                | Weekend                                  |
| `->mondays()` … `->sundays()` | Giorno specifico                         |
| `->days([0, 3])`              | Più giorni (0 = domenica, 3 = mercoledì) |

```php theme={null}
use Illuminate\Support\Facades;
use Illuminate\Console\Scheduling\Schedule;

Facades\Schedule::command('emails:send')
    ->hourly()
    ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
```

## Prevenzione sovrapposizioni

Con `withoutOverlapping` la prossima esecuzione attende la fine della precedente.

```php theme={null}
Schedule::command('emails:send')->withoutOverlapping();
```

Durata del lock in minuti (default 24h):

```php theme={null}
Schedule::command('emails:send')->withoutOverlapping(10);
```

<Info>
  Il lock usa la cache. Per svuotarlo:

  ```shell theme={null}
  php artisan schedule:clear-cache
  ```
</Info>

## Esecuzione su più server

Con `onOneServer` solo una macchina esegue il task.

```php theme={null}
Schedule::command('report:generate')
    ->fridays()
    ->at('17:00')
    ->onOneServer();
```

<Warning>
  Richiede driver cache `database`, `memcached`, `dynamodb` o `redis` condiviso.
</Warning>

### Flusso di controllo distribuito

```mermaid theme={null}
flowchart TD
    A["Più server eseguono<br>artisan schedule:run insieme"] --> B["Server 1"]
    A --> C["Server 2"]
    A --> D["Server 3"]

    B --> E["Tentativo di lock atomico<br>sulla cache condivisa"]
    C --> E
    D --> E

    E -->|"Primo a ottenerlo"| F["Esegue il task"]
    E -->|"Altri"| G["Salta"]

    F --> H["Rilascia il lock a fine esecuzione"]
```

## Raggruppare i task

```php theme={null}
Schedule::daily()
    ->onOneServer()
    ->timezone('Europe/Rome')
    ->group(function () {
        Schedule::command('emails:send --force');
        Schedule::command('emails:prune');
    });
```

## Esecuzione in background

I task pianificati alla stessa ora vengono eseguiti in ordine.
Con `runInBackground` li esegui in parallelo.

```php theme={null}
Schedule::command('analytics:report')
    ->daily()
    ->runInBackground();
```

<Warning>
  Solo per `command` ed `exec`.
</Warning>

## Modalità manutenzione

In manutenzione i task pianificati non partono. Per forzarli:

```php theme={null}
Schedule::command('emails:send')->evenInMaintenanceMode();
```

```mermaid theme={null}
flowchart TD
    A["artisan schedule:run"] --> B{"App in manutenzione?"}
    B -->|"No"| C["Esegue normalmente"]
    B -->|"Sì"| D{"evenInMaintenanceMode()?"}
    D -->|"Sì"| C
    D -->|"No"| E["Salta"]
```

## Pausa dello scheduler

```shell theme={null}
php artisan schedule:pause
php artisan schedule:continue
```

Con `evenWhenPaused` alcuni task continuano.

```php theme={null}
Schedule::command('emails:send')->evenWhenPaused();
```

## Output

### Su file

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->sendOutputTo(storage_path('logs/emails-send.log'));
```

`appendOutputTo` accoda.

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->appendOutputTo(storage_path('logs/emails-send.log'));
```

### Via email

```php theme={null}
Schedule::command('report:generate')
    ->daily()
    ->sendOutputTo($filePath)
    ->emailOutputTo('admin@example.com');
```

Solo fallimento:

```php theme={null}
Schedule::command('report:generate')
    ->daily()
    ->emailOutputOnFailure('admin@example.com');
```

## Hook

```php theme={null}
Schedule::command('emails:send')
    ->daily()
    ->before(function () {
        // Prima
    })
    ->after(function () {
        // Dopo
    });
```

Successo/fallimento:

```php theme={null}
use Illuminate\Support\Stringable;

Schedule::command('emails:send')
    ->daily()
    ->onSuccess(function (Stringable $output) {
        // Successo
    })
    ->onFailure(function (Stringable $output) {
        // Fallimento
    });
```

## Deploy sul server

<Steps>
  <Step title="Aggiungi una voce cron">
    ```shell theme={null}
    * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
    ```

    Modifica con `crontab -e`.
  </Step>

  <Step title="Verifica">
    ```shell theme={null}
    php artisan schedule:list
    ```
  </Step>
</Steps>

<Tip>
  [Laravel Cloud](https://cloud.laravel.com) gestisce lo scheduling senza configurare cron.
</Tip>

### In sviluppo locale

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

Foreground, chiama lo scheduler ogni minuto finché non premi `Ctrl+C`.

### Sub-minuto

```php theme={null}
Schedule::call(function () {
    DB::table('recent_users')->delete();
})->everySecond();
```

`schedule:run` resta attivo per completare tutti i task sub-minuto.

<Tip>
  Delega ai job in coda per non bloccare l'esecuzione.
</Tip>

Per interrompere in deploy:

```shell theme={null}
php artisan schedule:interrupt
```

## Comandi comuni

```shell theme={null}
php artisan schedule:list
php artisan schedule:run
php artisan schedule:work
php artisan schedule:pause
php artisan schedule:continue
php artisan schedule:clear-cache
php artisan schedule:interrupt
```


## Related topics

- [Console Artisan](/it/artisan.md)
- [Tutorial - Laravel Console Starter](/it/packages/laravel-console-starter/tutorial.md)
- [Laravel Cloud Hibernation (sospensione automatica)](/it/blog/laravel-cloud-hibernation.md)
- [Loop dell'agente](/it/packages/laravel-copilot-sdk/agent-loop.md)
- [Laravel Octane](/it/octane.md)
