O Console Artisan do Laravel e Comandos Personalizados
Artisan é o "canivete suíço" do Laravel — com mais de 100 comandos integrados cobrindo todas as operações e tarefas de manutenção, e comandos personalizados que permitem lidar com tarefas repetitivas com um único clique.
1. O Que Você Vai Aprender
- Lista completa de comandos integrados: migrate/cache/config/route/list
- Criando Comandos Personalizados:
make:commande Sintaxe Signature - Argumentos e Opções de Comando: Definição {argument} / {--option}
- Agendamento de Comandos: Tarefas Cron em app/Console/Kernel.php
- Comandos interativos: confirm/choice/ask/anticipate
2. Uma História Real de um Engenheiro de Operações
(1) Dor: Executar manualmente 20 tarefas de operações todos os dias
Toda manhã, Charlie tem que executar manualmente as seguintes tarefas: limpar sessões expiradas, gerar relatórios diários, sincronizar status de assinaturas, enviar emails de lembrete de expiração, fazer backup do banco de dados... Vinte tarefas espalhadas por cinco janelas de terminal — se ele esquecer uma, terá que pagar do bolso. Bob perguntou: "Essas não são todas tarefas agendadas? Por que você precisa executá-las manualmente?"
(2) Soluções com Comandos Artisan e Agendamento
Comandos Artisan personalizados envolvem tarefas repetitivas em um único comando, e o Schedule executa-as automaticamente conforme agendado — Charlie só precisa dar uma olhada no log de execução uma vez por dia.
# Um comando faz tudo
php artisan shopmetrics:daily-maintenance
# Ou deixe o agendador executar automaticamente às 2 da manhã
php artisan schedule:run
(3) Resultado
Depois que Charlie configurou o agendamento com Artisan, todas as 20 tarefas executaram automaticamente, e a taxa de falhas caiu de 5% para 0%.
3. Lista Completa de Comandos Integrados
(1) Categorias de Comandos Comuns
| Categoria | Comando | Descrição |
|---|---|---|
| Aplicação | about |
Visão Geral de Informações do Ambiente |
down / up |
Alternar Modo de Manutenção | |
env |
Exibir configuração .env | |
| Banco de Dados | migrate |
Executar Migration |
migrate:rollback |
Reverter Migration | |
migrate:fresh |
Reconstruir Banco de Dados | |
db:seed |
Preenchimento de Dados | |
db:show |
Informações do Banco de Dados | |
| Cache | cache:clear |
Limpar Cache |
config:cache / clear |
Cache de Configuração | |
route:cache / clear |
Cache de Rotas | |
view:cache / clear |
Cache de Views | |
| Rotas | route:list |
Listar todas as rotas |
| Fila | queue:work |
Iniciar Worker |
queue:failed |
Ver Tarefas Falhadas | |
queue:retry |
Retentar tarefas falhadas | |
| Gerar | make:model |
Criar Model |
make:controller |
Criar Controller | |
make:migration |
Criar Migration | |
make:command |
Criar Command |
(1) ▶ Exemplo: Referência Rápida de Comandos Artisan Comuns
# Informações da app
php artisan about
php artisan env
# Operações de banco de dados
php artisan migrate --seed
php artisan db:show --counts
# Gerenciamento de cache
php artisan cache:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan optimize # Cache config + route + view
# Modo de manutenção
php artisan down --secret="maintenance-token" # Permitir acesso com ?secret=
php artisan up
# Gerenciamento de fila
php artisan queue:work --queue=high,default
php artisan queue:failed
php artisan queue:retry all
# Inspeção de rotas
php artisan route:list --path=api
php artisan route:list --columns=method,uri,name
Saída:
# Comando executado com sucesso
4. Comandos Personalizados
(1) Criar Comando
php artisan make:command ProcessTenantAnalytics
# Cria: app/Console/Commands/ProcessTenantAnalytics.php
(2) Sintaxe Signature do Comando
// app/Console/Commands/ProcessTenantAnalytics.php
class ProcessTenantAnalytics extends Command
{
// Sintaxe Signature: {argument} {--option}
protected $signature = 'shopmetrics:analytics
{tenant? : ID ou slug do Tenant (opcional)}
{--type=monthly : Tipo de relatório (daily|weekly|monthly)}
{--force : Forçar recálculo}
{--format=csv : Formato de saída (csv|json)}';
protected $description = 'Processar analytics para tenants';
public function handle(): int
{
$tenant = $this->argument('tenant');
$type = $this->option('type');
$force = $this->option('force');
if ($tenant) {
$this->processSingleTenant($tenant, $type, $force);
} else {
$this->processAllTenants($type, $force);
}
return self::SUCCESS;
}
}
(3) Regras da Sintaxe Signature
| Sintaxe | Descrição | Exemplo |
|---|---|---|
{name} |
Parâmetro obrigatório | {tenant} |
{name?} |
Parâmetros opcionais | {tenant?} |
{name=default} |
Com valor padrão | {type=monthly} |
{--option} |
Opção Booleana | --force |
{--option=default} |
Opções com Valores | --format=csv |
{--O|shortcut} |
Opções curtas | --force|f |
(1) ▶ Exemplo: Comandos de Gerenciamento de Tenants do ShopMetrics
// app/Console/Commands/ManageTenant.php
class ManageTenant extends Command
{
protected $signature = 'shopmetrics:tenant
{action : Ação a executar (list|suspend|activate|stats)}
{tenant? : ID ou slug do Tenant}
{--with-users : Incluir estatísticas de usuários}';
protected $description = 'Gerenciar tenants do ShopMetrics';
public function handle(): int
{
$action = $this->argument('action');
match ($action) {
'list' => $this->listTenants(),
'suspend' => $this->suspendTenant(),
'activate' => $this->activateTenant(),
'stats' => $this->showTenantStats(),
default => $this->error("Ação desconhecida: {$action}"),
};
return self::SUCCESS;
}
private function listTenants(): void
{
$tenants = Tenant::withCount(['shops', 'users'])->get();
$this->table(
['ID', 'Nome', 'Slug', 'Status', 'Lojas', 'Usuários'],
$tenants->map(fn ($t) => [
$t->id, $t->name, $t->slug, $t->status,
$t->shops_count, $t->users_count,
])
);
}
private function suspendTenant(): void
{
$identifier = $this->argument('tenant') ?? $this->ask('Digite o ID ou slug do tenant:');
$tenant = $this->resolveTenant($identifier);
$tenant->update(['status' => 'suspended']);
$this->info("Tenant {$tenant->name} foi suspenso.");
}
private function resolveTenant(string $identifier): Tenant
{
return is_numeric($identifier)
? Tenant::findOrFail($identifier)
: Tenant::whereSlug($identifier)->firstOrFail();
}
}
Saída:
// Execução bem-sucedida
5. Argumentos e Opções de Comando
(1) Recuperar Parâmetros
// Argumentos
$name = $this->argument('name'); // Argumento único
$all = $this->arguments(); // Todos os argumentos como array
// Opções
$force = $this->option('force'); // Opção única (booleano ou valor)
$all = $this->options(); // Todas as opções como array
(2) Parâmetros de Array
// Signature com argumento de array
protected $signature = 'shopmetrics:report
{tenants* : Um ou mais IDs de tenant}
{--type=monthly}';
// Uso
php artisan shopmetrics:report 1 2 3 --type=weekly
// Acesso
$tenants = $this->argument('tenants'); // [1, 2, 3]
(3) Métodos de Saída
| Método | Descrição | Saída de Exemplo |
|---|---|---|
info() |
Informação Verde | ✓ Concluído |
error() |
Erro Vermelho | ✗ Falhou |
warn() |
Aviso Amarelo | ⚠ Aviso |
line() |
Texto simples | Texto simples |
table() |
Tabela | Tabela Formatada |
progressBar() |
Barra de Progresso | ██████░░ 60% |
(1) ▶ Exemplo: Comando de limpeza de dados do ShopMetrics com barra de progresso
// app/Console/Commands/CleanupExpiredData.php
class CleanupExpiredData extends Command
{
protected $signature = 'shopmetrics:cleanup
{--days=90 : Excluir dados mais antigos que N dias}
{--dry-run : Mostrar o que seria excluído}';
protected $description = 'Limpar dados expirados (relatórios antigos, tokens expirados)';
public function handle(): int
{
$days = $this->option('days');
$dryRun = $this->option('dry-run');
$cutoff = now()->subDays($days);
// Limpar tokens expirados
$expiredTokens = Sanctum::$personalAccessTokenModel::where('last_used_at', '<', $cutoff);
$this->info(($dryRun ? 'Excluiria' : 'Excluindo') . " {$expiredTokens->count()} tokens expirados.");
// Limpar relatórios antigos do S3
$bar = $this->output->createProgressBar(Tenant::count());
$deletedFiles = 0;
Tenant::chunk(100, function ($tenants) use ($cutoff, $dryRun, &$deletedFiles, $bar) {
foreach ($tenants as $tenant) {
$files = Storage::disk('s3')->allFiles("reports/{$tenant->slug}");
foreach ($files as $file) {
if (Storage::disk('s3')->lastModified($file) < $cutoff->timestamp) {
if (!$dryRun) Storage::disk('s3')->delete($file);
$deletedFiles++;
}
}
$bar->advance();
}
});
$bar->finish();
$this->newLine();
$this->info(($dryRun ? 'Excluiria' : 'Excluídos') . " {$deletedFiles} arquivos de relatório antigos.");
if (!$dryRun) {
$expiredTokens->delete();
}
return self::SUCCESS;
}
}
Saída:
// Execução bem-sucedida
6. Agendamento de Comandos
(1) Definição de Agendamento
// routes/console.php
use Illuminate\Support\Facades\Schedule;
Schedule::command('shopmetrics:analytics --type=daily')
->dailyAt('02:00')
->onOneServer()
->withoutOverlapping()
->emailOutputOnFailure('admin@shopmetrics.io');
Schedule::command('shopmetrics:cleanup --days=90')
->weekly()
->sundays()
->at('03:00')
->onOneServer();
Schedule::command('shopmetrics:sync-subscriptions')
->dailyAt('06:00')
->onOneServer()
->withoutOverlapping();
Schedule::job(new ProcessMonthlyAnalyticsJob)
->monthlyOn(1, '00:00')
->onOneServer();
(2) Opções de Frequência de Agendamento
| Método | Frequência | Cron Equivalente |
|---|---|---|
everyMinute() |
Por Minuto | * * * * * |
everyFiveMinutes() |
A cada 5 minutos | */5 * * * * |
hourly() |
Por hora | 0 * * * * |
daily() |
Meia-noite todos os dias | 0 0 * * * |
dailyAt('14:00') |
14:00 diariamente | 0 14 * * * |
weekly() |
Todo domingo à meia-noite | 0 0 * * 0 |
monthly() |
Meia-noite no dia 1 de cada mês | 0 0 1 * * |
cron('...') |
Cron Personalizado | Qualquer |
(3) Restrições de Agendamento
| Método | Descrição |
|---|---|
onOneServer() |
Executar em apenas um servidor |
withoutOverlapping() |
Execução sobreposta não é permitida |
runInBackground() |
Executar em segundo plano |
when(Closure) |
Execução Condicional |
environments('prod') |
Ambiente Especificado |
emailOutputOnFailure() |
Notificação por email em caso de falha |
(1) ▶ Exemplo: Configuração Completa de Agendamento do ShopMetrics
// routes/console.php
Schedule::command('shopmetrics:analytics --type=daily')
->dailyAt('02:00')
->onOneServer()
->withoutOverlapping(60)
->emailOutputOnFailure('ops@shopmetrics.io');
Schedule::command('shopmetrics:cleanup --days=90')
->weeklyOn(Schedule::SUNDAY, '03:00')
->onOneServer();
Schedule::command('shopmetrics:send-expiring-notifications')
->dailyAt('08:00')
->when(fn () => Subscription::expiringSoon()->exists());
Schedule::command('queue:prune-failed --hours=168')
->daily();
Schedule::command('queue:prune-batches --hours=168')
->daily();
// Entrada Cron no servidor
// * * * * * cd /var/www/shopmetrics && php artisan schedule:run >> /dev/null 2>&1
Saída:
// Execução bem-sucedida
7. Comandos Interativos
(1) Métodos de Interação
// Pedir entrada
$name = $this->ask('Qual é o nome do tenant?');
// Pedir com padrão
$email = $this->ask('Endereço de email?', 'admin@example.com');
// Entrada secreta (senhas)
$password = $this->secret('Digite a senha:');
// Confirmar (sim/não)
if ($this->confirm('Deseja continuar?', true)) {
// Padrão: sim
}
// Escolha (seleção única)
$type = $this->choice(
'Selecione o tipo de relatório',
['daily', 'weekly', 'monthly'],
0 // índice padrão
);
// Antecipar (autocompletar)
$name = $this->anticipate('Nome do tenant', Tenant::pluck('name')->toArray());
(2) Fluxo de Trabalho Interativo Compreensivo
// app/Console/Commands/SetupTenant.php
class SetupTenant extends Command
{
protected $signature = 'shopmetrics:tenant-setup';
protected $description = 'Assistente interativo de configuração de tenant';
public function handle(): int
{
$this->info('=== Assistente de Configuração de Tenant ShopMetrics ===');
$name = $this->ask('Nome do tenant');
$slug = $this->anticipate('Slug', [Str::slug($name)]);
$domain = $this->ask('Domínio personalizado (opcional)', $slug . '.shopmetrics.io');
$plan = $this->choice('Selecione o plano', ['Starter', 'Pro', 'Enterprise'], 1);
$ownerEmail = $this->ask('Email do proprietário');
$this->table(
['Campo', 'Valor'],
[['Nome', $name], ['Slug', $slug], ['Domínio', $domain], ['Plano', $plan], ['Proprietário', $ownerEmail]],
);
if (!$this->confirm('Criar este tenant?', true)) {
$this->warn('Cancelado.');
return self::FAILURE;
}
$tenant = Tenant::create(compact('name', 'slug', 'domain'));
User::factory()->create([
'tenant_id' => $tenant->id,
'email' => $ownerEmail,
'role' => 'tenant_owner',
]);
$this->info("Tenant {$name} criado com sucesso!");
return self::SUCCESS;
}
}
(1) ▶ Exemplo: Comando Interativo de Exportação de Dados do ShopMetrics
// app/Console/Commands/ExportData.php
class ExportData extends Command
{
protected $signature = 'shopmetrics:export';
protected $description = 'Ferramenta interativa de exportação de dados';
public function handle(): int
{
$type = $this->choice('O que exportar?', [
'orders' => 'Pedidos',
'products' => 'Produtos',
'analytics' => 'Relatório de Analytics',
]);
$tenant = $this->anticipate('Tenant (deixe em branco para todos)', Tenant::pluck('name')->push('Todos')->toArray());
$format = $this->choice('Formato?', ['csv', 'xlsx', 'json'], 0);
$dateFrom = $this->ask('Data de (Y-m-d, opcional)');
$dateTo = $this->ask('Data até (Y-m-d, opcional)');
$this->info("Exportando {$type} para {$tenant} no formato {$format}...");
$query = match ($type) {
'orders' => Order::query(),
'products' => Product::query(),
'analytics' => AnalyticsReport::query(),
};
if ($tenant !== 'Todos') {
$tenantModel = Tenant::whereName($tenant)->firstOrFail();
$query->where('tenant_id', $tenantModel->id);
}
if ($dateFrom) $query->where('created_at', '>=', $dateFrom);
if ($dateTo) $query->where('created_at', '<=', $dateTo);
$count = $query->count();
$this->info("Encontrados {$count} registros.");
if (!$this->confirm("Exportar {$count} registros?", true)) {
return self::FAILURE;
}
$path = ExportService::export($query, $format);
$this->info("Exportação salva em: {$path}");
return self::SUCCESS;
}
}
Saída:
// Execução bem-sucedida
8. Exemplo Compreensivo: Conjunto de Comandos de Operações do ShopMetrics
// ============================================
// Compreensivo: Comandos de Operações do ShopMetrics
// Abrange: signature, options, progress, scheduling, interactive
// ============================================
// app/Console/Commands/DailyMaintenance.php
class DailyMaintenance extends Command
{
protected $signature = 'shopmetrics:daily-maintenance
{--skip-analytics : Pular processamento de analytics}
{--skip-cleanup : Pular limpeza de dados}
{--notify : Enviar notificação de conclusão}';
protected $description = 'Executar tarefas de manutenção diária';
public function handle(): int
{
$this->info('Iniciando manutenção diária...');
if (!$this->option('skip-analytics')) {
$this->processAnalytics();
}
if (!$this->option('skip-cleanup')) {
$this->cleanupExpiredData();
}
$this->syncSubscriptions();
if ($this->option('notify')) {
$this->sendCompletionNotification();
}
$this->info('Manutenção diária concluída.');
return self::SUCCESS;
}
private function processAnalytics(): void
{
$this->info('Processando analytics diários...');
$tenants = Tenant::active()->get();
$bar = $this->output->createProgressBar($tenants->count());
foreach ($tenants as $tenant) {
GenerateReportJob::dispatch($tenant, 'daily', 'json');
$bar->advance();
}
$bar->finish();
$this->newLine();
}
private function cleanupExpiredData(): void
{
$this->call('shopmetrics:cleanup', ['--days' => 90]);
}
private function syncSubscriptions(): void
{
$this->call('shopmetrics:sync-subscriptions');
}
private function sendCompletionNotification(): void
{
$admin = User::where('role', 'super_admin')->first();
$admin?->notify(new DailyMaintenanceCompleted());
}
}
// Agendar
// routes/console.php
Schedule::command('shopmetrics:daily-maintenance --notify')
->dailyAt('02:00')
->onOneServer()
->withoutOverlapping()
->emailOutputOnFailure('ops@shopmetrics.io');
❓ Perguntas Frequentes
make:command e make:command --command?--command=xxx Especifica um nome de comando personalizado: php artisan make:command SendEmails --command=emails:send. Se omitido, o nome padrão app:console-commands:send-emails é usado. É recomendado sempre especificar um nome de comando personalizado.* * * * * php artisan schedule:run. Todas as tarefas são definidas no código, tornando-as versionáveis e testáveis. Não configure um job Cron separado para cada tarefa.$this->artisan('command:name', ['arg' => 'value']) para executar o comando em um teste e verifique que a saída é ->expectsOutput('Done') ou verifique as mudanças no banco de dados.--dry-run para pré-visualizar os resultados antes da execução.->appendOutputTo(storage_path('logs/command.log')) para anexar logs no agendador; use Log::info() dentro do comando; ou use emailOutputOnFailure() para enviar uma notificação por email em caso de falha.📖 Resumo
- Artisan inclui mais de 100 comandos integrados cobrindo operações como migrations, cache, filas e rotas
- Comandos personalizados usam sintaxe signature para definir parâmetros e opções
- Use Laravel Schedule para automatizar o agendamento de comandos em vez de jobs Cron manuais
- Comandos interativos usam
ask,confirmechoicepara fornecer uma experiência guiada - Saída em tabela e barras de progresso tornam os comandos mais profissionais
- onOneServer/withoutOverlapping: Previne múltiplas instâncias executando simultaneamente
📝 Exercícios
-
Exercício Básico (⭐): Crie um comando chamado
shopmetrics:tenant-statsque aceita um parâmetrotenante exibe o número de lojas, pedidos e receita total desse tenant, formatado usando o comandotable. -
Exercício Avançado (⭐⭐): Crie o comando
shopmetrics:daily-maintenance, que inclui processamento de analytics (com barra de progresso) + limpeza de dados expirados + sincronização de assinaturas, e configure o Schedule para executar automaticamente às 2:00 da manhã todos os dias. -
Desafio (⭐⭐⭐): Implemente um comando assistente interativo
shopmetrics:tenant-setup— insira o nome do tenant, slug, plano e endereço de email do administrador sequencialmente, com validação em cada etapa, e finalmente confirme a criação. Inclua sugestões de autocompletar (anticipate) para o nome do tenant.



