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!