• Introduzione
  • Definizione dei programmi
    • Comandi Artisan di programmazione
    • Programmazione dei lavori in coda
    • Comandi Shell di programmazione
    • Opzioni di frequenza di programmazione
    • Timezones
    • Evitare sovrapposizioni di compiti
    • Eseguire i compiti su un server
    • Task in background
    • Modalità manutenzione
  • Eseguire lo Scheduler
    • Eseguire lo Scheduler localmente
  • Uscita dei compiti
  • Ganci per i compiti

Introduzione

In passato, potresti aver scritto una voce di configurazione cron per ogni compito che avevi bisogno di programmare sul tuo server. Tuttavia, questo può diventare rapidamente una seccatura perché la pianificazione delle attività non è più nel controllo dei sorgenti e devi entrare con SSH nel tuo server per vedere le voci di cron esistenti o aggiungere voci aggiuntive.

Lo schedulatore di comandi di Laravel offre un nuovo approccio alla gestione delle attività programmate sul tuo server. Lo scheduler ti permette di definire in modo fluido ed espressivo la tua pianificazione dei comandi all’interno della tua applicazione Laravel stessa. Quando usi lo scheduler, è necessaria solo una singola voce cron sul tuo server. Il tuo programma di attività è definito nel metodo schedule del file app/Console/Kernel.php. Per aiutarti a iniziare, un semplice esempio è definito all’interno del metodo.

Definizione degli scheduler

Puoi definire tutti i tuoi compiti programmati nel metodo schedule della classe App\Console\Kernel della tua applicazione. Per iniziare, diamo un’occhiata ad un esempio. In questo esempio, programmeremo una chiusura da chiamare ogni giorno a mezzanotte. All’interno della chiusura eseguiremo una query al database per cancellare una tabella:

<?phpnamespace App\Console;use Illuminate\Console\Scheduling\Schedule;use Illuminate\Foundation\Console\Kernel as ConsoleKernel;use Illuminate\Support\Facades\DB;class Kernel extends ConsoleKernel{ /** * The Artisan commands provided by your application. * * @var array */ protected $commands = ; /** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->call(function () { DB::table('recent_users')->delete(); })->daily(); }}

Oltre a programmare usando le chiusure, puoi anche programmare oggetti invocabili. Gli oggetti invocabili sono semplici classi PHP che contengono un metodo __invoke:

$schedule->call(new DeleteRecentUsers)->daily();

Se vuoi visualizzare una panoramica delle tue attività pianificate e la prossima volta che devono essere eseguite, puoi usare il comando schedule:list Artisan:

php artisan schedule:list

Scheduling Artisan Commands

Oltre a programmare le chiusure, puoi anche programmare i comandi Artisan e i comandi di sistema. Per esempio, puoi usare il metodo command per pianificare un comando Artisan usando sia il nome del comando che la classe.

Quando pianifichi i comandi Artisan usando il nome della classe del comando, puoi passare un array di argomenti addizionali della linea di comando che dovrebbero essere forniti al comando quando viene invocato:

use App\Console\Commands\SendEmailsCommand;$schedule->command('emails:send Taylor --force')->daily();$schedule->command(SendEmailsCommand::class, )->daily();

Scheduling Queued Jobs

Il metodo job può essere usato per pianificare un lavoro in coda. Questo metodo fornisce un modo conveniente per programmare lavori in coda senza usare il metodo call per definire le chiusure per accodare il lavoro:

use App\Jobs\Heartbeat;$schedule->job(new Heartbeat)->everyFiveMinutes();

I secondi e terzi argomenti opzionali possono essere forniti al metodo job che specifica il nome della coda e la connessione della coda che dovrebbe essere usata per accodare il lavoro:

use App\Jobs\Heartbeat;// Dispatch the job to the "heartbeats" queue on the "sqs" connection...$schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();

Comandi Shell di programmazione

Il metodo exec può essere usato per inviare un comando al sistema operativo:

$schedule->exec('node /home/forge/script.js')->daily();

Opzioni di frequenza di programmazione

Abbiamo già visto alcuni esempi di come si può configurare un compito da eseguire a intervalli specifici. Tuttavia, ci sono molte altre frequenze di pianificazione che puoi assegnare a un’attività:

Metodo Descrizione
->cron('* * * * *'); Esegui il compito su una pianificazione cron personalizzata
->everyMinute(); Esegui il compito ogni minuto
->everyTwoMinutes(); Esegui il compito ogni due minuti
->everyThreeMinutes(); Esegui il compito ogni tre minuti
->everyFourMinutes(); Esegui il compito ogni quattro minuti
->everyFiveMinutes(); Esegui il compito ogni cinque minuti
->everyTenMinutes(); Esegui il compito ogni dieci minuti
->everyFifteenMinutes(); Esegui il compito ogni quindici minuti
->everyThirtyMinutes(); Esegui il compito ogni trenta minuti
->hourly(); Esegui il compito ogni ora
->hourlyAt(17); Esegui il compito ogni ora a 17 minuti dopo l’ora
->everyTwoHours(); Esegui il compito ogni due ore
->everyThreeHours(); Esegui il compito ogni tre ore
->everyFourHours(); Esegui il compito ogni quattro ore
->everySixHours(); Esegui il compito ogni sei ore
->daily(); Esegui il compito ogni giorno a mezzanotte
->dailyAt('13:00'); Esegui il compito ogni giorno alle 13:00
->twiceDaily(1, 13); Esegui il compito ogni giorno alle 1:00 & 13:00
->weekly(); Esegui il compito ogni domenica alle 00:00
->weeklyOn(1, '8:00'); Esegui il compito ogni settimana il lunedì alle 8:00
->monthly(); Esegui il compito il primo giorno di ogni mese alle 00:00
->monthlyOn(4, '15:00'); Esegui il compito ogni mese il 4 alle 15:00
->twiceMonthly(1, 16, '13:00'); Esegui il compito mensilmente il 1 e il 16 alle 13:00
->lastDayOfMonth('15:00'); Esegui il compito l’ultimo giorno del mese alle 15:00
->quarterly(); Esegui il task il primo giorno di ogni trimestre alle 00:00
->yearly(); Esegui il compito il primo giorno di ogni anno alle 00:00
->yearlyOn(6, 1, '17:00'); Esegui il compito ogni anno il primo giugno alle 17:00
->timezone('America/New_York'); Imposta il fuso orario per il compito

Questi metodi possono essere combinati con vincoli aggiuntivi per creare pianificazioni ancora più precise che vengono eseguite solo in certi giorni della settimana. Per esempio, puoi programmare un comando da eseguire settimanalmente il lunedì:

// Run once per week on Monday at 1 PM...$schedule->call(function () { //})->weekly()->mondays()->at('13:00');// Run hourly from 8 AM to 5 PM on weekdays...$schedule->command('foo') ->weekdays() ->hourly() ->timezone('America/Chicago') ->between('8:00', '17:00');

Un elenco di vincoli di pianificazione aggiuntivi può essere trovato qui sotto:

Metodo Descrizione
->weekdays(); Limitare il compito ai giorni della settimana
->weekends(); Limita il compito ai fine settimana
->sundays(); Limita il compito alla domenica
->mondays(); Limita il compito a lunedì
->tuesdays(); Limita il compito a martedì
->wednesdays(); Limita il compito a mercoledì
->thursdays(); Limita il compito a giovedì
->fridays(); Limita il compito a venerdì
->saturdays(); Limita il compito al sabato
->days(array|mixed); Limita il compito a giorni specifici
->between($startTime, $endTime); Limitare l’esecuzione del compito tra le ore di inizio e fine
->unlessBetween($startTime, $endTime); Limitare l’esecuzione del compito tra le ore di inizio e fine
->when(Closure); Limitare il compito basato su un test di verità
->environments($env); Limitare il compito a specifici ambienti

Costrizioni di giorno

Il metodo days può essere usato per limitare l’esecuzione di un compito a specifici giorni della settimana. Per esempio, puoi programmare un comando da eseguire ogni ora la domenica e il mercoledì:

$schedule->command('emails:send') ->hourly() ->days();

In alternativa, puoi usare le costanti disponibili sulla classe Illuminate\Console\Scheduling\Schedule quando definisci i giorni in cui un compito dovrebbe essere eseguito:

use Illuminate\Console\Scheduling\Schedule;$schedule->command('emails:send') ->hourly() ->days();

Between Time Constraints

Il metodo between può essere usato per limitare l’esecuzione di un compito in base all’ora del giorno:

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

Similmente, il metodo unlessBetween può essere usato per escludere l’esecuzione di un compito per un periodo di tempo:

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

Truth Test Constraints

Il metodo when può essere usato per limitare l’esecuzione di un compito basato sul risultato di un dato test di verità. In altre parole, se la chiusura data restituisce true, il compito verrà eseguito fino a quando nessun’altra condizione vincolante impedirà l’esecuzione del compito:

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

Il metodo skip può essere visto come l’inverso di when. Se il metodo skip restituisce true, il compito programmato non verrà eseguito:

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

Quando si usano metodi when concatenati, il comando programmato verrà eseguito solo se tutte le condizioni when restituiscono true.

Costrizioni d’ambiente

Il metodo environments può essere usato per eseguire i compiti solo su determinati ambienti (come definito dalla variabile d’ambiente APP_ENV):

$schedule->command('emails:send') ->daily() ->environments();

Timezones

Utilizzando il metodo timezone, puoi specificare che l’orario di un compito programmato deve essere interpretato all’interno di un dato fuso orario:

$schedule->command('report:generate') ->timezone('America/New_York') ->at('2:00')

Se assegni ripetutamente lo stesso fuso orario a tutti i tuoi compiti programmati, potresti voler definire un metodo scheduleTimezone nella tua classe App\Console\Kernel. Questo metodo dovrebbe restituire il fuso orario predefinito che dovrebbe essere assegnato a tutte le attività programmate:

/** * Get the timezone that should be used by default for scheduled events. * * @return \DateTimeZone|string|null */protected function scheduleTimezone(){ return 'America/Chicago';}

{note} Ricorda che alcuni fusi orari utilizzano l’ora legale. Quando cambia l’ora legale, il tuo compito programmato potrebbe essere eseguito due volte o addirittura non essere eseguito affatto. Per questa ragione, raccomandiamo di evitare la programmazione per fusi orari quando possibile.

Evitare la sovrapposizione dei compiti

Per impostazione predefinita, i compiti programmati saranno eseguiti anche se l’istanza precedente del compito è ancora in esecuzione. Per evitare questo, puoi usare il metodo withoutOverlapping:

$schedule->command('emails:send')->withoutOverlapping();

In questo esempio, il comando emails:send Artisan sarà eseguito ogni minuto se non è già in esecuzione. Il metodo withoutOverlapping è particolarmente utile se avete compiti che variano drasticamente nel loro tempo di esecuzione, impedendovi di prevedere esattamente quanto tempo un dato compito richiederà.

Se necessario, potete specificare quanti minuti devono passare prima che il blocco “senza sovrapposizione” scada. Per impostazione predefinita, il blocco scade dopo 24 ore:

$schedule->command('emails:send')->withoutOverlapping(10);

Esecuzione dei compiti su un server

{note} Per utilizzare questa caratteristica, la tua applicazione deve utilizzare il driver cache database, memcached, dynamodb o redis come driver cache predefinito della tua applicazione. Inoltre, tutti i server devono comunicare con lo stesso server di cache centrale.

Se lo scheduler della tua applicazione è in esecuzione su più server, puoi limitare un lavoro pianificato per eseguirlo solo su un singolo server. Per esempio, supponiamo che abbiate un’attività pianificata che genera un nuovo rapporto ogni venerdì sera. Se il task scheduler è in esecuzione su tre server worker, il task pianificato verrà eseguito su tutti e tre i server e genererà il report tre volte. Non va bene!

Per indicare che il compito deve essere eseguito solo su un server, usa il metodo onOneServer quando definisci il compito programmato. Il primo server che otterrà il compito si assicurerà un blocco atomico sul lavoro per evitare che altri server eseguano lo stesso compito allo stesso tempo:

$schedule->command('report:generate') ->fridays() ->at('17:00') ->onOneServer();

Task in background

Per impostazione predefinita, più compiti programmati allo stesso tempo verranno eseguiti in sequenza in base all’ordine in cui sono definiti nel tuo metodo schedule. Se hai compiti di lunga durata, questo può causare che i compiti successivi inizino molto più tardi del previsto. Se vuoi eseguire i compiti in background in modo che possano essere eseguiti tutti simultaneamente, puoi usare il metodo runInBackground:

$schedule->command('analytics:report') ->daily() ->runInBackground();

{note} Il metodo runInBackground può essere usato solo quando si programmano i compiti tramite i metodi command e exec.

Modalità manutenzione

I compiti programmati della tua applicazione non verranno eseguiti quando l’applicazione è in modalità manutenzione, poiché non vogliamo che i tuoi compiti interferiscano con qualsiasi manutenzione non terminata che potresti eseguire sul tuo server. Tuttavia, se vuoi forzare l’esecuzione di un’attività anche in modalità di manutenzione, puoi chiamare il metodo evenInMaintenanceMode quando definisci l’attività:

$schedule->command('emails:send')->evenInMaintenanceMode();

Running The Scheduler

Ora che abbiamo imparato come definire le attività pianificate, parliamo di come eseguirle effettivamente sul nostro server. Il comando schedule:run Artisan valuterà tutti i tuoi compiti programmati e determinerà se devono essere eseguiti in base all’ora corrente del server.

Quindi, quando usiamo lo scheduler di Laravel, abbiamo solo bisogno di aggiungere una singola voce di configurazione cron al nostro server che esegua il comando schedule:run ogni minuto. Se non sai come aggiungere voci di cron al tuo server, considera di usare un servizio come Laravel Forge che può gestire le voci di cron per te:

* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

Eseguire lo scheduler localmente

In genere, non dovresti aggiungere una voce di cron dello scheduler alla tua macchina di sviluppo locale. Invece, puoi usare il comando schedule:work Artisan. Questo comando verrà eseguito in primo piano e invocherà lo scheduler ogni minuto fino a quando non terminerai il comando:

php artisan schedule:work

Task Output

Lo scheduler Laravel fornisce diversi metodi convenienti per lavorare con l’output generato dai task programmati. Per prima cosa, usando il metodo sendOutputTo, puoi inviare l’output a un file per un controllo successivo:

$schedule->command('emails:send') ->daily() ->sendOutputTo($filePath);

Se vuoi aggiungere l’output a un dato file, puoi usare il metodo appendOutputTo:

$schedule->command('emails:send') ->daily() ->appendOutputTo($filePath);

Utilizzando il metodo emailOutputTo, puoi inviare l’output a un indirizzo email di tua scelta. Prima di inviare l’output di un task via email, dovresti configurare i servizi email di Laravel:

$schedule->command('report:generate') ->daily() ->sendOutputTo($filePath) ->emailOutputTo('');

Se vuoi inviare l’output via email solo se il comando Artisan o di sistema programmato termina con un codice di uscita diverso da zero, usa il metodo emailOutputOnFailure:

$schedule->command('report:generate') ->daily() ->emailOutputOnFailure('');

{note} I metodi emailOutputTo, emailOutputOnFailure, sendOutputTo e appendOutputTo sono esclusivi dei metodi command e exec.

Ganci per task

Utilizzando i metodi before e after, è possibile specificare il codice da eseguire prima e dopo l’esecuzione del task programmato:

$schedule->command('emails:send') ->daily() ->before(function () { // The task is about to execute... }) ->after(function () { // The task has executed... });

I metodi onSuccess e onFailure permettono di specificare il codice da eseguire se il task programmato ha successo o fallisce. Un fallimento indica che il comando programmato di Artisan o di sistema è terminato con un codice di uscita diverso da zero:

$schedule->command('emails:send') ->daily() ->onSuccess(function () { // The task succeeded... }) ->onFailure(function () { // The task failed... });

Se l’output è disponibile dal vostro comando, potete accedervi nei vostri after, onSuccess o onFailure hooks indicando un’istanza Illuminate\Support\Stringable come argomento $output della definizione della chiusura del vostro hook:

use Illuminate\Support\Stringable;$schedule->command('emails:send') ->daily() ->onSuccess(function (Stringable $output) { // The task succeeded... }) ->onFailure(function (Stringable $output) { // The task failed... });

Ping degli URL

Utilizzando i metodi pingBefore e thenPing, lo scheduler può eseguire automaticamente il ping di un dato URL prima o dopo l’esecuzione di un compito. Questo metodo è utile per notificare un servizio esterno, come Envoyer, che il tuo compito programmato sta iniziando o ha finito l’esecuzione:

$schedule->command('emails:send') ->daily() ->pingBefore($url) ->thenPing($url);

I metodi pingBeforeIf e thenPingIf possono essere usati per eseguire il ping di un dato URL solo se una data condizione è true:

$schedule->command('emails:send') ->daily() ->pingBeforeIf($condition, $url) ->thenPingIf($condition, $url);

I metodi pingOnSuccess e pingOnFailure possono essere usati per eseguire il ping di un dato URL solo se il compito ha successo o fallisce. Un fallimento indica che il comando Artisan o di sistema programmato è terminato con un codice di uscita non zero:

$schedule->command('emails:send') ->daily() ->pingOnSuccess($successUrl) ->pingOnFailure($failureUrl);

Tutti i metodi di ping richiedono la libreria Guzzle HTTP. Guzzle è tipicamente installato in tutti i nuovi progetti Laravel per impostazione predefinita, ma, puoi installare manualmente Guzzle nel tuo progetto usando il gestore di pacchetti Composer se è stato accidentalmente rimosso:

composer require guzzlehttp/guzzle