Mengapa Laravel? Kita Sudah Punya NestJS!
Pertanyaan yang selalu muncul. Mengapa pakai dua bahasa backend dalam satu proyek? Jawabannya pragmatis, bukan dogmatis.
Membangun admin panel di React/Next.js memakan waktu berminggu-minggu. Anda perlu tabel dengan sorting, filtering, pagination. Form dengan validasi. Upload file. Dashboard widget. User management. RBAC.
Membangun admin panel yang sama di Laravel + Filament memakan waktu berhari-hari. Filament memberikan semua itu out-of-the-box.
| Tugas | React (custom) | Laravel + Filament |
|---|---|---|
| Tabel CRUD dengan filter | 2-3 hari | 30 menit |
| Form dengan validasi | 1-2 hari | 15 menit |
| Dashboard widget | 1-2 hari | 20 menit |
| Total | 2-3 minggu | 2-3 hari |
Keputusan Senior: Gunakan alat yang tepat untuk pekerjaan yang tepat. Developer senior tahu kapan mencampur teknologi — dan kapan tidak.
Apa itu Laravel?
Laravel adalah framework PHP yang mengikuti pola MVC (Model-View-Controller).
- Model = Data Anda. Model
Drivermerepresentasikan tabeldriversdi database. - View = Apa yang dilihat pengguna. Filament menangani ini otomatis.
- Controller = Polisi lalu lintas. Menerima request, memanggil service yang tepat.
Setup Laravel dengan Filament
composer create-project laravel/laravel fleet-admin
cd fleet-admin
composer require filament/filament
php artisan filament:install --panels
Membuat Modul Manajemen Driver
Langkah 1: Migration
Schema::create('drivers', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('name', 100);
$table->string('email')->unique();
$table->string('phone', 20);
$table->string('license_number', 50)->unique();
$table->date('license_expiry');
$table->enum('status', ['active', 'inactive', 'suspended'])->default('active');
$table->date('hired_at');
$table->timestamps();
});
Langkah 2: Eloquent Model
class Driver extends Model
{
use HasUuids;
protected $fillable = [
'name', 'email', 'phone',
'license_number', 'license_expiry', 'status', 'hired_at',
];
// Eloquent Scopes — filter query yang reusable
public function scopeActive($query)
{
return $query->where('status', 'active');
}
public function scopeExpiringLicense($query, int $days = 30)
{
return $query->where('license_expiry', '<=', now()->addDays($days));
}
}
Mengapa Eloquent powerful: Perhatikan scopeActive(). Sekarang di manapun dalam kode, kita bisa menulis Driver::active()->get() alih-alih mengulang where('status', 'active') di mana-mana.
Langkah 3: Filament Resource
class DriverResource extends Resource
{
protected static ?string $model = Driver::class;
protected static ?string $navigationIcon = 'heroicon-o-user-group';
public static function form(Forms\Form $form): Forms\Form
{
return $form->schema([
Forms\Components\TextInput::make('name')->required()->maxLength(100),
Forms\Components\TextInput::make('email')->email()->required()->unique(ignoreRecord: true),
Forms\Components\TextInput::make('license_number')->required()->unique(ignoreRecord: true),
Forms\Components\DatePicker::make('license_expiry')->required(),
Forms\Components\Select::make('status')
->options(['active' => 'Aktif', 'inactive' => 'Nonaktif', 'suspended' => 'Ditangguhkan'])
->required(),
]);
}
public static function table(Tables\Table $table): Tables\Table
{
return $table->columns([
Tables\Columns\TextColumn::make('name')->searchable()->sortable(),
Tables\Columns\BadgeColumn::make('status')
->colors(['success' => 'active', 'warning' => 'inactive', 'danger' => 'suspended']),
Tables\Columns\TextColumn::make('license_expiry')->date(),
]);
}
}
Hanya ~40 baris PHP dan kita punya admin panel lengkap dengan search, sort, filter, create, edit, dan delete. Coba bangun itu di React.
Pola Service Layer
Banyak developer Laravel menaruh semua logika bisnis di controller. Ini anti-pattern:
// ❌ Buruk — "Fat Controller"
public function suspend(string $id)
{
$driver = Driver::findOrFail($id);
$driver->update(['status' => 'suspended']);
Mail::to($driver->email)->send(new DriverSuspendedMail($driver));
AuditLog::create(['action' => 'driver_suspended', 'driver_id' => $id]);
Vehicle::where('current_driver_id', $id)->update(['current_driver_id' => null]);
}
// ✅ Baik — Service + Event Pattern
class DriverService
{
public function suspend(string $driverId): Driver
{
$driver = Driver::findOrFail($driverId);
$driver->update(['status' => 'suspended']);
event(new DriverSuspended($driver)); // Biarkan listener menangani efek samping
return $driver;
}
}
Dengan event-driven approach, setiap listener melakukan satu hal: kirim email, catat audit, unassign kendaraan. Mudah dites, mudah diperluas.
Kapan Laravel Lebih Unggul dari Node.js
| Skenario | Pilihan Lebih Baik | Mengapa |
|---|---|---|
| Admin panel | Laravel | Filament tidak tertandingi |
| Generate PDF | Laravel | DomPDF/Snappy sudah matang |
| Queue/job | Laravel | Sistem queue bawaan |
| API Real-time | NestJS | Performa async lebih baik |
| WebSocket | NestJS | Dukungan async native |
Perspektif Senior: Kubu "PHP sudah mati" itu salah. PHP 8.3+ cepat, typed, dan modern. Laravel 11+ adalah salah satu framework terbaik di bahasa apapun.
Selanjutnya
Di Part 5, kita akan mendesain arsitektur database — mengapa kita pakai PostgreSQL untuk telemetri, MySQL untuk data bisnis, dan Redis untuk caching.
Ini adalah Part 4 dari seri Fleet Management System. Memilih alat yang tepat untuk setiap pekerjaan — bahkan jika itu berarti mencampur bahasa — adalah tanda penilaian engineering senior.
