Criando Comandos no Laravel
Se você já trabalhou com o Laravel, certamente percebeu a praticidade de seus comandos artisan
durante o desenvolvimento. É provável que já tenha empregado comandos como artisan make:model
ou artisan make:controller
, o que aumenta significativamente a eficiência no trabalho. Contudo, alguns desenvolvedores desconhecem a possibilidade de criar comandos personalizados para automatizar tarefas específicas em seus sistemas.
Gerando comandos no Laravel
Criar um comando no Laravel é uma tarefa bastante simples – você só precisa utilizar o seguinte comando:
php artisan make:command SendNotification
O comando mencionado acima resultará na criação do arquivo app/Console/Commands/SendNotification.php
, o qual conterá o seguinte conteúdo:
class SendNotification extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:send-notification';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
}
}
Ele contém dois atributos: $signature
e $description
e um método handle()
. O atributo $signature
representa o nome e a assinatura (com argumentos, se houver) que será usado para invocar o comando pelo artisan
. Já o atributo $description
deve conter uma descrição sucinta do comando, que será exibida ao listar todos os comandos disponíveis. No método handle()
é onde implementaremos as ações que o nosso comando executará. Segue uma implementação simples.
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use App\Notifications\EmailNotification;
class SendNotification extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:send-notification';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Envia notificações para os usuários do sistema';
/**
* Execute the console command.
*/
public function handle()
{
User::all()
->each(function ($user) {
$user->notify(new EmailNotification(
subject: 'Olá',
body: 'Olá, ' . $user->name . ' como vai você!'
));
});
}
}
Ao executar o comando php artisan list
“, o comando será listado com a signature e description informados.
Dessa forma, podemos executar nosso comando utilizando php artisan app:send-notification
.
Ao ser executado o comando, o método handle(
) será chamado automaticamente pelo framework, então podemos incluir qualquer dependência nele, que o mecanismo de injeção de dependências do Laravel cuidará de injetá-la para nós.
public function handle(MyService $myService)
{
$result = $myService->doSomething();
$this->info($result);
}
Uma alternativa à criação de uma classe para um comando é utilizar uma closure. Para isso, utilizamos o método command
da classe Artisan
, chamando-o em routes/console.php
, o qual é carregado automaticamente juntamente com as classes de comando pelo app/Console/Kernel.php
. Vamos observar como nosso comando seria implementado como uma closure.
Artisan::command('app:send-notification', function () {
User::all()
->each(function ($user) {
$user->notify(new EmailNotification(
subject: 'Olá',
body: 'Olá, ' . $user->name . ' como vai você!'
));
});
})->purpose('Envia notificações para os usuários do sistema');
No método command
, o primeiro parâmetro é a assinatura do comando, e o segundo é a closure que será executada. Temos a opção de encadear o método purpose()
para adicionar a descrição do comando.
Entrada de dados
Quando criamos um comando muitas vezes é necessário ele receber alguns valores para executar a tarefa. Podemos fazer isso de duas formas: de argumentos na chamada do comando ou perguntando para o usuario.
Argumentos na chamada do comando
Para receber valores como argumentos, primeiro devemos colocá-los entre chaves na assinatura do comando, por exemplo, app:send-notification {subject} {body}
, e depois recuperá-los dentro do método handle()
utilizando o método $this->argument(<nome na assinatura>)
. Veja um exemplo.
protected $signature = 'app:send-notification {subject} {body}';
public function handle()
{
$subject = $this->argument('subject');
$body = $this->argument('body');
User::all()
->each(function ($user) use($subject, $body) {
$user->notify(new EmailNotification(
subject: $subject,
body: str_replace( '%name%', $user->name, $body)
));
});
}
Agora, podemos executar o comando passando os argumentos usando: php artisan app:send-notification "Mensagem de Olá!" "%name%, tenha um bom dia."
. No entanto, é importante lembrar que, a partir de agora, se você não fornecer os argumentos necessários para o comando, ele resultará em um erro.
É possível tornar os argumentos opcionais adicionando “?” após seus nomes na assinatura (app:send-notification {subject?} {body?}
), o que faz com que o método $this->argument('subject')
retorne null, caso não seja fornecido. Além disso, é possível fornecer valores padrão para os argumentos na assinatura, indicando o nome seguido pelo sinal de “=” e o valor a ser atribuído caso nenhum seja explicitamente passado.
// argumentos com valores opcionais
app:send-notification {subject?} {body?}
//argumentos com valores padrões
app:send-notification {subject="Mensagem de Olá"} {body="Olá %name%, prazer em te conhecer"}
Uma alternativa para a entrada de argumentos é o option
, que é especificado com dois traços “—” antes do nome do argumento e pode ou não ter um valor associado. Se não tiver um valor, ele atua como um interruptor de ligar/desligar, fazendo com que o método $this->option('nome do option')
retorne true ou false. Ao adicionar um “=” no final do option (por exemplo, –queue=), podemos fornecer valores para o argumento option, tratando-o como um argumento normal, e recuperá-lo usando o método $this->option()
.
protected $signature = 'app:send-notification {subject} {body} {--log}';
public function handle()
{
$subject = $this->argument('subject');
$body = $this->argument('body');
User::all()
->each(function ($user) use($subject, $body) {
if($this->option('log')) {
$this->info('Enviando email para '. $user->email);
}
$user->notify(new EmailNotification(
subject: $subject,
body: str_replace( '%name%', $user->name, $body)
));
});
}
Se necessário, é possível adicionar descrições aos argumentos do comando usando dois pontos “:” entre o nome do argumento e a sua descrição. É importante observar que deve haver um espaço entre os dois pontos, o nome e a descrição do argumento do comando.
app:send-notification {subject : Assunto da Mensagem} {body : O texto da mensagem} {--log : Exibe os emails enviados}
Pedindo argumentos obrigatórios
Quando lidamos com argumentos obrigatórios, podemos implementar a interface PromptsForMissingInput
. Dessa forma, se o usuário não fornecer os argumentos ao chamar o comando, será solicitado a inserir os valores necessários.
class SendNotification extends Command implements PromptsForMissingInput
{
//...
}
Perguntando para o usuário
Em vez de receber valores por meio de argumentos, podemos interagir com o usuário fazendo perguntas e aguardando uma resposta. A classe de comando oferece diversos métodos para essa finalidade, sendo o mais simples o $this->ask(<Texto explicativo>)
, que exibe uma mensagem e espera que o usuário insira um texto. Opcionalmente, podemos fornecer um valor padrão, que será utilizado caso o usuário não forneça nenhuma entrada.
$subject = $this->ask('Informe o assunto da mensagem: ');
// ou
$subject = $this->ask('Informe o assunto da mensagem: ', 'Mensagem de Teste');
Se for necessário solicitar ao usuário a inserção de informações sensíveis que não devem ser exibidas na tela, podemos utilizar o método $this->secret(<mensagem>)
. Esse método opera de maneira semelhante ao ask, porém não exibe na tela o que o usuário está digitando.
Para questionamentos de confirmação, como o clássico “Deseja continuar?”, empregamos o método $this->confirm(<mensagem>)
, que retorna um valor booleano. Como segundo parâmetro, podemos fornecer um booleano para estabelecer o valor padrão, caso o usuário não forneça nenhuma resposta.
if( $this->confirm('Deseja continuar?') ){
$this->info('continuando');
}else{
$this->error('cancelando');
}
Podemos ter o recurso de auto-completar utilizando o método $this->anticipate()
. Nesse caso, fornecemos a mensagem a ser exibida e um array contendo os valores a serem auto-completados.
$users = User::all()->pluck('name');
$name = $this->anticipate('Para qual usuario deseja enviar?', $users);
Para apresentar uma lista de opções para que o usuário escolha, utilizamos o método $this->choice(<mensagem>, <opções>, <índice selecionado por padrão>)
. No primeiro argumento, inserimos a mensagem a ser exibida; no segundo, um array contendo as opções disponíveis; e no terceiro, o índice no array do elemento que será o valor padrão caso o usuário não informe nenhum valor.
$users = User::all()->pluck('email');
$email = $this->choice(
'Escolha para quem enviar a notificação?',
$users->toArray(),
0
);
$this->info($email);
Escrevendo no console
Para exibir dados no console, dispomos dos seguintes métodos: line()
, info()
, comment()
, question()
, warn()
e error()
. Cada um deles possui a cor apropriada para o seu propósito. Além disso, temos o método $this->newline()
, que insere uma ou mais linhas em branco, dependendo do parâmetro fornecido.
$this->line("Iniciando envio de e-mails.");
$this->info("O e-mail foi enviado.");
$this->comment("Um comentário.");
$this->question("Pergunta?");
$this->newline(2);
$this->error("Ocorreu um erro ao enviar o e-mail.");
Podemos apresentar os dados em forma de tabela de maneira bastante simples utilizando o método $this->table()
. Este método recebe um array com o cabeçalho (título das colunas) da tabela e uma matriz contendo os dados a serem exibidos.
$users = User::all(['name', 'email'])->toArray();
$this->table(['Nome', 'Email'], $users);
Outro componente para exibição de dados é a barra de progresso, especialmente útil quando lidamos com tarefas demoradas e desejamos informar sobre o progresso. Para isso, criamos um objeto ProgressBar
utilizando o método $this->output->createProgressBar()
, fornecendo o número total de “passos” que a barra de progresso deve ter. Em seguida, chamamos o método advance()
para avançar um passo na barra de progresso.
$users = User::all();
$bar = $this->output->createProgressBar(count($users));
$users->each(function ($user) use($subject, $body, $bar) {
$user->notify(new EmailNotification(
subject: $subject,
body: str_replace( '%name%', $user->name, $body)
));
$bar->advance();
});
Chamando comandos programaticamente
Também é possível chamar comandos através de um controller ou service utilizando a Facade Artisan. Isso é feito invocando o método call()
, onde o primeiro parâmetro é o comando a ser executado, e o segundo é um array contendo os argumentos do comando.
Artisan::call('app:send-notification', ['subject' => 'titulo', 'body' => 'corpo do email']);
Bem, esta foi uma breve introdução ao Artisan Laravel, um recurso muito interessante do framework que possibilita a automação de diversas tarefas nos nossos sistemas. Esses comandos podem ser facilmente integrados ao cron do sistema para execução de tarefas agendadas. Espero que tenha sido de alguma ajuda. T++.