Laravel Artisan Command: Cara Membuat Custom Command untuk Task Otomatis
Apa Itu Laravel Artisan Command?
Laravel Artisan Command adalah perintah CLI yang disediakan Laravel untuk menjalankan berbagai pekerjaan dari terminal.
Selain command bawaan seperti:
php artisan migrate
atau:
php artisan cache:clear
Laravel juga memungkinkan kita membuat Custom Artisan Command sendiri.
Contohnya:
php artisan cleanup:temporary-data
atau:
php artisan report:daily
Command custom dapat digunakan untuk:
- Membersihkan data lama
- Import data
- Export data
- Sinkronisasi API
- Generate laporan
- Memproses database
- Maintenance aplikasi
- Menjalankan task otomatis
- Menjadi bagian dari Laravel Scheduler
Konsep sederhananya:
Terminal / Cron / Scheduler
↓
Artisan Command
↓
Application
↓
Database / API / File
Kenapa Membuat Custom Artisan Command?
Tanpa Artisan Command, developer mungkin menjalankan script PHP secara terpisah:
php script-cleanup.php
Masalahnya, script tersebut berada di luar struktur Laravel dan harus mengatur sendiri:
- Database connection
- Configuration
- Environment
- Dependency
- Logging
- Service aplikasi
Dengan Artisan Command, kita dapat menggunakan ekosistem Laravel secara langsung.
Misalnya:
DB::table('users')->count();
atau:
Log::info('Cleanup selesai');
atau:
User::where(...)->get();
Semua dapat digunakan di dalam Command.
Membuat Artisan Command
Gunakan:
php artisan make:command CleanupTemporaryData
Laravel akan membuat file Command.
Pada struktur Laravel yang umum, file tersebut berada di:
app/Console/Commands/CleanupTemporaryData.php
Command kemudian dapat dipanggil dengan:
php artisan cleanup:temporary-data
Struktur Custom Command
Contoh sederhana:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class CleanupTemporaryData extends Command
{
protected $signature = 'cleanup:temporary-data';
protected $description = 'Membersihkan data sementara';
public function handle()
{
$this->info('Cleanup selesai.');
return self::SUCCESS;
}
}
Ada beberapa bagian penting:
$signature
$description
handle()
Apa Itu $signature?
Property:
protected $signature = 'cleanup:temporary-data';
menentukan nama command.
Jadi command tersebut dipanggil dengan:
php artisan cleanup:temporary-data
Format yang umum digunakan adalah:
kategori:aksi
Contohnya:
user:cleanup
report:generate
order:sync
invoice:send
product:import
Gunakan nama yang jelas agar mudah dipahami ketika command semakin banyak.
Apa Itu $description?
Property:
protected $description = 'Membersihkan data sementara';
digunakan untuk menjelaskan fungsi command.
Jika menjalankan:
php artisan list
description tersebut akan membantu developer mengetahui fungsi command.
Apa Fungsi handle()?
Method:
public function handle()
{
//
}
merupakan tempat utama logic command dijalankan.
Contoh:
public function handle()
{
$this->info('Memulai cleanup...');
// proses cleanup
$this->info('Cleanup selesai.');
return self::SUCCESS;
}
Ketika command dijalankan:
php artisan cleanup:temporary-data
Laravel akan mengeksekusi:
handle()
Menampilkan Informasi di Terminal
Artisan menyediakan beberapa helper untuk memberikan output.
Contoh:
$this->info('Proses berhasil.');
Output:
INFO Proses berhasil.
Untuk pesan error:
$this->error('Proses gagal.');
Untuk warning:
$this->warn('Data tidak ditemukan.');
Untuk pesan biasa:
$this->line('Memproses data...');
Output terminal menjadi lebih informatif.
Contoh Command dengan Database
Misalnya kita ingin membuat Command untuk menghitung jumlah user.
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
class CountUsers extends Command
{
protected $signature = 'user:count';
protected $description = 'Menampilkan jumlah user';
public function handle()
{
$total = User::count();
$this->info("Total user: {$total}");
return self::SUCCESS;
}
}
Jalankan:
php artisan user:count
Misalnya hasilnya:
Total user: 1250
Menggunakan Query Builder
Tidak harus menggunakan Eloquent.
Kita juga dapat menggunakan Query Builder:
use Illuminate\Support\Facades\DB;
$total = DB::table('users')->count();
$this->info("Total user: {$total}");
Artisan Command memiliki akses ke database Laravel seperti bagian aplikasi lainnya.
Membuat Command untuk Cleanup Data
Misalnya terdapat tabel:
temporary_data
dan kita ingin menghapus data yang lebih lama dari 7 hari.
use App\Models\TemporaryData;
public function handle()
{
$deleted = TemporaryData::where(
'created_at',
'<',
now()->subDays(7)
)->delete();
$this->info("{$deleted} data berhasil dihapus.");
return self::SUCCESS;
}
Jalankan:
php artisan cleanup:temporary-data
Output:
125 data berhasil dihapus.
Command seperti ini sangat cocok dikombinasikan dengan Scheduler.
Menghubungkan Artisan Command dengan Scheduler
Setelah Command dapat dijalankan:
php artisan cleanup:temporary-data
kita dapat menjadwalkannya.
Contoh:
use Illuminate\Support\Facades\Schedule;
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00');
Alurnya:
Cron
↓
Laravel Scheduler
↓
cleanup:temporary-data
↓
Database Cleanup
Dengan begitu cleanup dapat berjalan otomatis setiap hari.
Artisan Command Bisa Dijalankan Manual dan Otomatis
Ini salah satu keuntungan utama.
Command:
php artisan cleanup:temporary-data
dapat dijalankan manual ketika diperlukan.
Tetapi juga dapat dijalankan otomatis:
Scheduler
↓
cleanup:temporary-data
Jadi satu logic dapat digunakan untuk dua kebutuhan.
Menggunakan Argument
Terkadang Command membutuhkan input dari user.
Misalnya:
php artisan user:find 100
Untuk membuat argument:
protected $signature = 'user:find {id}';
Kemudian:
public function handle()
{
$id = $this->argument('id');
$this->info("Mencari user dengan ID: {$id}");
return self::SUCCESS;
}
Jalankan:
php artisan user:find 100
Output:
Mencari user dengan ID: 100
Argument Wajib
Dengan:
protected $signature = 'user:find {id}';
id merupakan argument wajib.
Jika menjalankan:
php artisan user:find
Laravel akan meminta argument tersebut.
Argument Opsional
Gunakan:
protected $signature = 'user:find {id?}';
Sekarang argument boleh tidak diberikan.
Contoh:
php artisan user:find
Dalam code:
$id = $this->argument('id');
if (!$id) {
$this->warn('ID belum diberikan.');
return self::FAILURE;
}
Memberikan Default Value
Argument juga dapat memiliki default value.
protected $signature = 'report:generate {date?}';
Kemudian:
$date = $this->argument('date') ?? now()->toDateString();
Jika menjalankan:
php artisan report:generate
command menggunakan tanggal hari ini.
Jika:
php artisan report:generate 2026-08-24
command menggunakan tanggal yang diberikan.
Menggunakan Option
Selain argument, Artisan juga mendukung option.
Misalnya:
php artisan report:generate --format=csv
Signature:
protected $signature = 'report:generate {--format=pdf}';
Ambil option:
$format = $this->option('format');
Jika tidak diberikan:
pdf
Jika diberikan:
php artisan report:generate --format=csv
hasil:
csv
Option Boolean
Option juga bisa berupa flag.
Misalnya:
protected $signature = 'user:cleanup {--force}';
Kemudian:
if ($this->option('force')) {
// jalankan mode force
}
Command:
php artisan user:cleanup --force
Option seperti --force berguna untuk tindakan yang berisiko.
Meminta Konfirmasi Sebelum Menjalankan Task
Untuk operasi berbahaya, kita dapat meminta konfirmasi.
if (!$this->confirm('Apakah Anda yakin ingin menghapus data?')) {
$this->warn('Proses dibatalkan.');
return self::SUCCESS;
}
Ketika command dijalankan:
Apakah Anda yakin ingin menghapus data? (yes/no)
Ini berguna ketika Command dijalankan secara manual.
Namun hati-hati jika Command juga digunakan oleh Scheduler.
Scheduler tidak dapat menjawab prompt interaktif seperti developer di terminal.
Untuk command otomatis, sebaiknya hindari interaksi yang membutuhkan input manual.
Gunakan --force untuk Automation
Misalnya:
protected $signature = 'data:cleanup {--force}';
Kemudian:
if (!$this->option('force')) {
if (!$this->confirm('Lanjutkan proses?')) {
return self::SUCCESS;
}
}
Saat manual:
php artisan data:cleanup
akan meminta konfirmasi.
Sedangkan Scheduler dapat menggunakan:
Schedule::command('data:cleanup --force')
->dailyAt('02:00');
Dengan begitu task otomatis tidak menunggu input.
Membuat Progress Bar
Untuk Command yang memproses banyak data, progress bar dapat membantu.
Contoh:
$users = User::query()->get();
$bar = $this->output->createProgressBar($users->count());
$bar->start();
foreach ($users as $user) {
// proses user
$bar->advance();
}
$bar->finish();
Output:
100/100 [============================] 100%
Ini sangat berguna ketika Command dijalankan secara manual.
Menggunakan Table Output
Jika ingin menampilkan data dalam bentuk tabel:
$this->table(
['ID', 'Name'],
[
[1, 'Budi'],
[2, 'Andi'],
]
);
Output menjadi lebih mudah dibaca:
+----+------+
| ID | Name |
+----+------+
| 1 | Budi |
| 2 | Andi |
+----+------+
Menangani Data dalam Jumlah Besar
Hindari:
$users = User::all();
foreach ($users as $user) {
// ...
}
jika tabel memiliki jutaan data.
Karena semua data dapat dimuat ke memory.
Lebih baik gunakan:
User::chunkById(500, function ($users) {
foreach ($users as $user) {
// proses
}
});
Atau gunakan lazyById() sesuai kebutuhan.
Konsepnya:
Database
↓
500 rows
↓
Process
↓
500 rows
↓
Process
↓
...
Memory lebih terkontrol.
Contoh Command dengan chunkById()
public function handle()
{
$processed = 0;
User::chunkById(500, function ($users) use (&$processed) {
foreach ($users as $user) {
// proses user
$processed++;
}
});
$this->info("{$processed} user berhasil diproses.");
return self::SUCCESS;
}
Ini lebih aman untuk data besar dibandingkan mengambil seluruh data sekaligus.
Gunakan Service untuk Logic yang Kompleks
Hindari membuat handle() menjadi terlalu panjang.
Contoh buruk:
public function handle()
{
// 300 baris logic
}
Lebih baik:
Command
↓
Service
↓
Repository / Model
↓
Database
Misalnya:
public function handle(ReportService $service)
{
$service->generateDailyReport();
$this->info('Report selesai.');
return self::SUCCESS;
}
Dengan demikian Command hanya bertugas sebagai entry point.
Artisan Command Sebaiknya Menjadi Entry Point
Idealnya:
Artisan Command
↓
Service
↓
Business Logic
Bukan:
Artisan Command
↓
Ratusan baris business logic
Keuntungannya, logic dapat digunakan kembali dari:
- Controller
- Queue Job
- Scheduler
- Command
- Test
Menggunakan Dependency Injection
Laravel memungkinkan dependency injection pada handle().
Misalnya:
public function handle(ReportService $service)
{
$service->generate();
return self::SUCCESS;
}
Laravel akan melakukan resolve dependency tersebut melalui service container.
Ini membuat Command lebih mudah diuji dan dipelihara.
Menangani Exception
Command dapat menangani error dengan try/catch.
public function handle()
{
try {
// proses
$this->info('Proses berhasil.');
return self::SUCCESS;
} catch (\Throwable $e) {
$this->error($e->getMessage());
return self::FAILURE;
}
}
Untuk production, jangan hanya menampilkan error.
Sebaiknya juga gunakan logging:
use Illuminate\Support\Facades\Log;
Log::error('Command gagal', [
'exception' => $e,
]);
Gunakan Exit Code yang Benar
Artisan menyediakan:
return self::SUCCESS;
untuk proses berhasil.
Sedangkan:
return self::FAILURE;
untuk proses gagal.
Ini penting karena command dapat dijalankan oleh:
- Cron
- Supervisor
- CI/CD
- Script deployment
- Monitoring
Sistem tersebut dapat mengetahui apakah command berhasil berdasarkan exit code.
Contoh Command yang Baik
public function handle(ReportService $service)
{
$this->info('Memulai generate report...');
try {
$service->generateDailyReport();
$this->info('Report berhasil dibuat.');
return self::SUCCESS;
} catch (\Throwable $e) {
report($e);
$this->error('Generate report gagal.');
return self::FAILURE;
}
}
Command tetap sederhana, sedangkan logic berada di service.
Artisan Command untuk Import Data
Contoh signature:
protected $signature = 'product:import {file}';
Kemudian:
$file = $this->argument('file');
if (!file_exists($file)) {
$this->error('File tidak ditemukan.');
return self::FAILURE;
}
Jalankan:
php artisan product:import products.csv
Alurnya:
CSV
↓
Artisan Command
↓
Validate
↓
Process
↓
Database
Untuk file besar, sebaiknya proses dilakukan secara bertahap agar memory tidak berlebihan.
Artisan Command untuk Sinkronisasi API
Contoh:
protected $signature = 'product:sync';
Kemudian:
public function handle(ProductSyncService $service)
{
$this->info('Memulai sinkronisasi...');
$service->sync();
$this->info('Sinkronisasi selesai.');
return self::SUCCESS;
}
Command:
php artisan product:sync
Kemudian dapat dijadwalkan:
Schedule::command('product:sync')
->everyTenMinutes();
Alurnya:
Cron
↓
Scheduler
↓
product:sync
↓
External API
↓
Database
Artisan Command untuk Maintenance
Contoh command:
cache:cleanup
file:cleanup
session:cleanup
report:generate
data:sync
notification:send
Semua dapat dibuat sebagai Command terpisah.
Ini membuat maintenance server dan aplikasi lebih terstruktur.
Artisan Command vs Queue Job
Keduanya sering terlihat mirip, tetapi memiliki fungsi berbeda.
Artisan Command:
Cara menjalankan pekerjaan
Queue Job:
Cara memproses pekerjaan di background
Misalnya:
Artisan Command
↓
Dispatch Job
↓
Redis
↓
Queue Worker
Contoh:
public function handle()
{
GenerateReport::dispatch();
$this->info('Report Job masuk queue.');
return self::SUCCESS;
}
Kapan Menggunakan Command?
Gunakan Artisan Command ketika pekerjaan:
- Perlu dijalankan dari terminal
- Perlu dijadwalkan
- Merupakan proses maintenance
- Membutuhkan input CLI
- Digunakan untuk import/export
- Digunakan untuk sinkronisasi
- Digunakan sebagai entry point automation
Kapan Menggunakan Queue Job?
Gunakan Queue Job ketika pekerjaan:
- Membutuhkan waktu lama
- Tidak perlu membuat user menunggu
- Dapat diproses di background
- Membutuhkan retry
- Membutuhkan delay
- Memiliki banyak item untuk diproses
Contohnya:
Command
↓
Dispatch Job
↓
Redis
↓
Worker
Command + Scheduler + Queue
Ketiga fitur dapat digunakan bersama.
Misalnya setiap hari pukul 02:00 kita ingin memproses 1 juta data.
Arsitektur yang baik:
Cron
↓
Scheduler
↓
Artisan Command
↓
Dispatch Jobs
↓
Redis
↓
Queue Worker
↓
Process Data
Command tidak perlu memproses 1 juta data secara langsung.
Command cukup membagi pekerjaan menjadi Job.
Contoh Command untuk Dispatch Job
public function handle()
{
User::query()
->select('id')
->chunkById(500, function ($users) {
foreach ($users as $user) {
ProcessUser::dispatch($user->id);
}
});
$this->info('Semua job berhasil dikirim ke queue.');
return self::SUCCESS;
}
Kemudian Scheduler:
Schedule::command('user:process')
->dailyAt('02:00');
Worker:
php artisan queue:work redis
Jika menggunakan Supervisor, Worker dapat dijaga agar tetap berjalan.
Jangan Membuat Command Menjadi Terlalu Berat
Misalnya:
public function handle()
{
// ambil jutaan data
// request API jutaan kali
// generate file besar
// kirim ribuan email
}
Ini berpotensi menyebabkan:
- Memory tinggi
- Timeout
- Proses lama
- Sulit retry
- Server terbebani
Lebih baik:
Command
↓
Dispatch Jobs
↓
Queue
↓
Worker
Logging pada Artisan Command
Gunakan Laravel Log untuk mencatat proses penting:
use Illuminate\Support\Facades\Log;
Log::info('Cleanup dimulai');
Log::info('Cleanup selesai', [
'deleted' => $deleted,
]);
Kemudian log dapat diperiksa:
tail -f storage/logs/laravel.log
Ini sangat berguna ketika Command dijalankan melalui Scheduler karena tidak ada developer yang melihat output terminal secara langsung.
Command yang Dipanggil Scheduler Tidak Boleh Bergantung pada Terminal
Hindari logic seperti:
$this->ask('Masukkan nama:');
jika Command tersebut akan dijalankan otomatis.
Scheduler tidak memiliki manusia yang menjawab pertanyaan tersebut.
Untuk automation, gunakan:
Argument
Option
Configuration
Database
Environment
Contoh:
php artisan report:generate --format=pdf
lebih cocok untuk automation dibandingkan meminta input interaktif.
Gunakan Environment Variable untuk Konfigurasi
Misalnya:
REPORT_FORMAT=pdf
Kemudian:
$format = config('report.format');
atau melalui konfigurasi aplikasi.
Dengan begitu Command tidak perlu memiliki nilai yang hardcoded.
Artisan Command di Production
Sebelum menjalankan Command di production:
php artisan report:daily
pastikan:
- Command sudah dites
- Database benar
- Environment benar
- Permission benar
- Logic memiliki error handling
- Tidak ada operasi berbahaya tanpa proteksi
- Logging tersedia
Untuk operasi destructive, gunakan option seperti:
php artisan data:cleanup --force
sehingga mode otomatis dapat dibedakan dari mode manual.
Testing Artisan Command
Laravel juga memungkinkan Command diuji melalui automated test.
Contoh:
$this->artisan('user:count')
->assertExitCode(0);
Untuk Command dengan output:
$this->artisan('user:count')
->expectsOutput('Total user: 10')
->assertExitCode(0);
Testing seperti ini membantu memastikan perubahan kode tidak merusak automation.
Struktur Command yang Disarankan
Untuk project yang semakin besar:
app/
├── Console/
│ └── Commands/
│ ├── CleanupTemporaryData.php
│ ├── GenerateDailyReport.php
│ └── SyncProducts.php
│
├── Jobs/
│ ├── ProcessUser.php
│ └── GenerateReport.php
│
└── Services/
├── ReportService.php
└── ProductSyncService.php
Alurnya:
Command
↓
Service
↓
Job
↓
Database/API
Tidak semua Command membutuhkan Service atau Job, tetapi struktur ini berguna ketika logic semakin kompleks.
Contoh Custom Command Lengkap
Berikut contoh yang lebih realistis:
<?php
namespace App\Console\Commands;
use App\Services\ReportService;
use Illuminate\Console\Command;
use Throwable;
class GenerateDailyReport extends Command
{
protected $signature = 'report:daily {--force}';
protected $description = 'Generate laporan harian';
public function handle(ReportService $service)
{
$this->info('Memulai generate laporan...');
try {
$service->generateDailyReport();
$this->info('Laporan berhasil dibuat.');
return self::SUCCESS;
} catch (Throwable $e) {
report($e);
$this->error('Generate laporan gagal.');
return self::FAILURE;
}
}
}
Command tersebut dapat dijalankan:
php artisan report:daily
dan kemudian dijadwalkan:
Schedule::command('report:daily')
->dailyAt('08:00')
->withoutOverlapping();
Alur Automation Lengkap
Dengan konfigurasi tersebut:
Ubuntu Cron
↓
schedule:run
↓
Laravel Scheduler
↓
report:daily
↓
ReportService
↓
Database / API
Jika report membutuhkan proses berat:
Ubuntu Cron
↓
Scheduler
↓
report:daily
↓
Dispatch Job
↓
Redis
↓
Queue Worker
↓
Generate Report
Troubleshooting Artisan Command
Jika Command tidak dapat dijalankan, mulai dari:
php artisan list
Pastikan Command muncul.
Kemudian:
php artisan report:daily
Jika gagal, periksa error.
Jika dijalankan melalui Scheduler:
php artisan schedule:list
Kemudian:
php artisan schedule:run
Jika manual berhasil tetapi Scheduler gagal, periksa konfigurasi Scheduler dan Cron.
Command Tidak Muncul di php artisan list
Periksa:
php artisan optimize:clear
Kemudian:
php artisan list
Jika tetap tidak muncul, periksa:
- Namespace
- Nama class
- File Command
- Signature
- Struktur project
- Konfigurasi Console Laravel
Pastikan class Command memiliki namespace yang sesuai dengan lokasi file.
Command Berhasil Manual tetapi Gagal di Cron
Ini sering terjadi karena environment berbeda.
Periksa:
which php
dan:
php -v
Kemudian gunakan absolute path pada Cron:
* * * * * /usr/bin/php /var/www/myapp/artisan schedule:run
Periksa juga .env, permission, working directory, dan user yang menjalankan Cron.
Best Practice Membuat Artisan Command
Beberapa prinsip yang sebaiknya digunakan:
- Gunakan nama command yang jelas
report:daily
product:sync
user:cleanup
- Buat
handle()tetap sederhana
Pindahkan business logic kompleks ke Service.
- Gunakan argument dan option
Jangan hardcode semua nilai.
- Gunakan exit code
return self::SUCCESS;
atau:
return self::FAILURE;
- Tambahkan logging
Terutama untuk Command otomatis.
- Pertimbangkan Queue
Untuk pekerjaan yang berat.
- Hindari prompt interaktif
Jika Command digunakan oleh Scheduler.
- Gunakan chunking
Untuk data dalam jumlah besar.
- Gunakan
withoutOverlapping()
Jika task tidak boleh berjalan bersamaan.
- Tes Command secara manual sebelum memasukkannya ke Scheduler
Ini menghemat banyak waktu saat troubleshooting.
Kesimpulan
Laravel Artisan Command merupakan cara yang praktis untuk membuat task custom yang dapat dijalankan melalui terminal maupun automation.
Command sederhana dapat dibuat dengan:
php artisan make:command CleanupTemporaryData
Kemudian tentukan signature:
protected $signature = 'cleanup:temporary-data';
dan logic:
public function handle()
{
// proses
return self::SUCCESS;
}
Command kemudian dapat dijalankan manual:
php artisan cleanup:temporary-data
atau dijadwalkan:
Schedule::command('cleanup:temporary-data')
->dailyAt('02:00');
Untuk proses yang berat, gunakan kombinasi:
Cron
↓
Laravel Scheduler
↓
Artisan Command
↓
Queue Job
↓
Redis
↓
Queue Worker
Dengan pendekatan ini, Artisan Command menjadi entry point untuk automation, Scheduler menentukan kapan pekerjaan dijalankan, dan Queue menangani pekerjaan berat di background.
Struktur tersebut sangat cocok untuk aplikasi Laravel production yang membutuhkan proses otomatis seperti cleanup data, sinkronisasi API, generate laporan, import data, dan maintenance rutin.

