Capa de post contendo um tablet com um aplicativo de calendário aberto, um bloco de notas ao lado do tablet e um teclado acima do tablet

Agendamento de tarefas com Laravel


guias

Agendamento de tarefas é algo que hora ou outra todo mundo que trabalha com backend vai precisar fazer e como não poderia ser diferente no Laravel já existe uma ferramenta pronta para esta tarefa, bora conhecer!

O que são cron jobs

Muitos conhecem o agendamento de tarefas como cron jobs, bastante popular em sistemas baseados em Linux e no Mac OS. No Windows também temos uma ferramenta para agendamento de tarefas: o Task Scheduler. Mas o que são estes agendamentos?

Basicamente estes “jobs” são encarregados de executar algum script ou comando em um determinado dia/horário e sua configuração tende a ser bem simples, abaixo segue exemplo de configuração de um cron job no Ubuntu para reiniciar o apache todos os dias a meia noite:

0 0 * * * systemctl restart apache2

Cada um dos 5 primeiros caracteres separados por um espaço representa um período de tempo na seguinte ordem: minuto, hora, dia (do mês), mês e dia da semana (seg, ter etc.). Como a ideia aqui não é falar muito sobre cron jobs vou deixar apenas esta pequena explicação e partir para o tópico do post: agendamento de tarefas com Laravel.

Agendamento de tarefas no Laravel

Para realizar agendamento de tarefas de maneira bem simples no Laravel podemos utilizar o método schedule na classe app\Console\Kernel, abaixo segue um exemplo básico para limpar a tabela de convite de usuários pendentes todo dia à meia noite:

// app\Console\Kernel.php

protected function schedule(Schedule $schedule)
{
    $schedule->call(fn() => DB::table('invites')->where('status', 'pending')->delete())
        ->daily();
}

Essa é a maneira mais fácil para executar uma tarefa agendada: passando um callback para o método call() da classe Schedule e encadeando essa chamada a uma periodicidade que no nosso caso foi daily.

Também é possível passar objetos que contenham o método __invoke() para o para o método call():

// app\Console\Kernel.php

protected function schedule(Schedule $schedule)
{
    $schedule->call(new InvitesCleaner)
        ->daily();
}

Periodicidade de agendamentos

Existe uma série de métodos pré-definidos no Laravel que nos permite realizar agendamentos em praticamente qualquer periodicidade, desde a cada minuto até uma vez ao ano. Caso nenhum dos métodos prontos atenda a necessidade, ainda assim é possível passar o parâmetro da periodicidade em formato de cron job para o método cron() da classe Schedule o que torna o agendamento de tarefas com Laravel ainda mais flexível.

Abaixo vamos ver algumas das periodicidades mais comuns. Para uma lista completa das periodicidades conferira a documentação oficial.

protected function schedule(Schedule $schedule)
{
    // utiliza o método __invoke do objeto InvitesCleaner todo dia à meia noite
    $schedule->call(new InvitesCleaner)
        ->daily();
    // utiliza o método __invoke do objeto InvitesCleaner todo dia à 1am
    $schedule->call(new InvitesCleaner)
        ->dailyAt('01:00');
    // gera um log `info` de hora em hora
    $schedule->call(fn () => Log::info('Uma hora se passou'))
        ->hourly();
    // gera um log `info` de hora em hora no minuto 30
    $schedule->call(fn () => Log::info('Uma hora se passou'))
        ->hourlyAt('30');
    // utiliza o método __invoke do objeto WeeklyNews todo domingo à meia noite
    $schedule->call(new WeeklyNews)
        ->weekly();
    // utiliza o método __invoke do objeto WeeklyNews toda segunda às 9am
    $schedule->call(new WeeklyNews)
        ->weeklyOn('1', '9:00');
}

Os métodos também podem ser combinados para criar agendamentos mais dinâmicos e flexíveis, abaixo segue um exemplo de uma mensagem muito importante que será gravada nos logs durante os finais de semana (weekends), de hora em hora (hourly) nos horários entre 9:00 e 18:00:


$schedule->call(fn() => Log::critical('Você não deveria estar trabalhando!')) ->weekends() ->hourly() ->between('9:00', '18:00');

Restrições condicionais e de ambiente

Também é possível fazer com que um agendamento seja executado ou não dado um “teste de verdade” (truth test) ou de acordo com o ambiente da aplicação (prod, dev, staging).

O agendamento abaixo só será executado caso o teste passado no callback do método when() retorne true:

$schedule->call(fn() ‘...’)
    ->daily()
    ->when(fn() => true);

O inverso também é possível, o comando abaixo não será executado caso o teste do callback do método skip() seja true:

$schedule->call(fn() ‘...’)
    ->daily()
    ->skip(fn() => true);

E por último, o agendamento abaixo só será executado no ambiente de produção (definido no arquivo .env):

$schedule->call(fn() ‘...’)
    ->daily()
    ->environments([‘production’]);

Agendamento de comandos artisan, jobs e shell scripts

Escrever callbacks toda hora pode não ser a tarefa mais prazerosa do mundo para definir agendamentos. Pensando nisso o Laravel nos permite utilizar a interface de agendamento também para comandos artisan e scripts shell (shell exec). Os demais métodos encadeados permanecem o mesmo e as periodicidades também.

Agendamento comandos Artisan

Vamos supor que temos um comando artisan com a signature email:send que enviará emails de acordo com alguma regra definida no comando artisan. Para realizar o agendamento desde comando basta utilizarmos o método command() do objeto Schedule e seguir com os encadeamentos de periodicidade conforme já vimos anteriormente:

$schedule->command('emails:send')
    ->daily();
//ou utilizando a classe e passando parâmetros
$schedule->command(EmailsCommand::class, ['Laraveling', '--force'])
    ->daily();

Agendamento de Jobs

Assim como para comandos artisan, também podemos agendar jobs e mandá-los para uma fila:

$schedule->job(new DumpDatabase)
    ->daily();

Agendamentos Shell

E se precisarmos agendar uma tarefa que não necessariamente faz parte do contexto do Laravel/PHP? Simples! O Laravel nos permite agendar a execução via shell (similar ao shell_exec()), vamos executar um script node:

$schedule->exec('node /home/myApp/script.js')
    ->dailyAt(‘15:00’);

Conclusão

Agendamentos de tarefas podem ser mais simples do que parecem utilizando a classe Schedule do Laravel e as periodicidades pré-definidas cobrem boa parte dos casos de uso, mesmo os casos não cobertos por métodos ainda podemos utilizar o modelo de cron(‘* * * * *’) e ainda com a possibilidade de utilizar comandos artisan e agendar jobs grande parte dos problemas já são resolvidos.

Espero ter ajudado a entender esta funcionalidade simples mas muito útil, bons agendamentos!

-- Up the Laravel's \o/

← Post Anterior
Próximo Post →