Vai al contenuto principale

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

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

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
Con schedule:list vedi task e prossime esecuzioni.
php artisan schedule:list

Flusso dello scheduler

Definizione dello schedule

Definisci in routes/console.php. In alternativa in withSchedule di bootstrap/app.php.
use Illuminate\Console\Scheduling\Schedule;

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

Tipi di task pianificabili

Comandi Artisan

Con command.
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.
Artisan::command('delete:recent-users', function () {
    DB::table('recent_users')->delete();
})->purpose('Delete recent users')->daily();

Job in coda

use App\Jobs\Heartbeat;
use Illuminate\Support\Facades\Schedule;

Schedule::job(new Heartbeat)->everyFiveMinutes();
Coda e connessione:
Schedule::job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();

Comandi shell

Schedule::exec('node /home/forge/script.js')->daily();

Closure

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->daily();
Oggetti invocabili:
Schedule::call(new DeleteRecentUsers)->daily();

Frequenza

MetodoDescrizione
->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

Schedule::command('emails:send')->cron('0 9 * * *');

Combinazione frequenza + giorno

Schedule::call(function () {
    // ...
})->weekly()->mondays()->at('13:00');

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

Timezone

Schedule::command('report:generate')
    ->timezone('Europe/Rome')
    ->at('9:00');
Globale in config/app.php:
'schedule_timezone' => 'Europe/Rome',
Con l’ora legale i task potrebbero eseguirsi due volte o non eseguirsi affatto. Consigliato UTC quando possibile.

Vincoli condizionali

when / skip

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

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

environments

Schedule::command('emails:send')
    ->daily()
    ->environments(['staging', 'production']);

Orario

Schedule::command('emails:send')
    ->hourly()
    ->between('7:00', '22:00');

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

Giorno

MetodoDescrizione
->weekdays()Feriali
->weekends()Weekend
->mondays()->sundays()Giorno specifico
->days([0, 3])Più giorni (0 = domenica, 3 = mercoledì)
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.
Schedule::command('emails:send')->withoutOverlapping();
Durata del lock in minuti (default 24h):
Schedule::command('emails:send')->withoutOverlapping(10);
Il lock usa la cache. Per svuotarlo:
php artisan schedule:clear-cache

Esecuzione su più server

Con onOneServer solo una macchina esegue il task.
Schedule::command('report:generate')
    ->fridays()
    ->at('17:00')
    ->onOneServer();
Richiede driver cache database, memcached, dynamodb o redis condiviso.

Flusso di controllo distribuito

Raggruppare i task

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.
Schedule::command('analytics:report')
    ->daily()
    ->runInBackground();
Solo per command ed exec.

Modalità manutenzione

In manutenzione i task pianificati non partono. Per forzarli:
Schedule::command('emails:send')->evenInMaintenanceMode();

Pausa dello scheduler

php artisan schedule:pause
php artisan schedule:continue
Con evenWhenPaused alcuni task continuano.
Schedule::command('emails:send')->evenWhenPaused();

Output

Su file

Schedule::command('emails:send')
    ->daily()
    ->sendOutputTo(storage_path('logs/emails-send.log'));
appendOutputTo accoda.
Schedule::command('emails:send')
    ->daily()
    ->appendOutputTo(storage_path('logs/emails-send.log'));

Via email

Schedule::command('report:generate')
    ->daily()
    ->sendOutputTo($filePath)
    ->emailOutputTo('[email protected]');
Solo fallimento:
Schedule::command('report:generate')
    ->daily()
    ->emailOutputOnFailure('[email protected]');

Hook

Schedule::command('emails:send')
    ->daily()
    ->before(function () {
        // Prima
    })
    ->after(function () {
        // Dopo
    });
Successo/fallimento:
use Illuminate\Support\Stringable;

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

Deploy sul server

1

Aggiungi una voce cron

* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Modifica con crontab -e.
2

Verifica

php artisan schedule:list
Laravel Cloud gestisce lo scheduling senza configurare cron.

In sviluppo locale

php artisan schedule:work
Foreground, chiama lo scheduler ogni minuto finché non premi Ctrl+C.

Sub-minuto

Schedule::call(function () {
    DB::table('recent_users')->delete();
})->everySecond();
schedule:run resta attivo per completare tutti i task sub-minuto.
Delega ai job in coda per non bloccare l’esecuzione.
Per interrompere in deploy:
php artisan schedule:interrupt

Comandi comuni

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
Ultima modifica il 13 luglio 2026