Artikel ini tersedia dalam Bahasa Inggris

🇬🇧 Read in English

14 Mei 2026

🇮🇩 Bahasa Indonesia

Fleet Management Part 4: Admin Panel & Logika Bisnis dengan Laravel

Membangun admin panel yang powerful dengan Laravel dan Filament. Pola service layer, Eloquent ORM, arsitektur event-driven, dan kapan Laravel lebih unggul dari Node.js.

3 min read

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.

TugasReact (custom)Laravel + Filament
Tabel CRUD dengan filter2-3 hari30 menit
Form dengan validasi1-2 hari15 menit
Dashboard widget1-2 hari20 menit
Total2-3 minggu2-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 Driver merepresentasikan tabel drivers di 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

SkenarioPilihan Lebih BaikMengapa
Admin panelLaravelFilament tidak tertandingi
Generate PDFLaravelDomPDF/Snappy sudah matang
Queue/jobLaravelSistem queue bawaan
API Real-timeNestJSPerforma async lebih baik
WebSocketNestJSDukungan 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.

Lanjut Membaca

Previous article thumbnail

← Artikel Sebelumnya

Fleet Management Part 3: Backend API with NestJS

Artikel Selanjutnya →

Fleet Management Part 5: Database Design — PostgreSQL, MySQL & Redis

Next article thumbnail