Laravel 12 best practices bisa diringkas menjadi lima kebiasaan: controller tipis, validasi di Form Request, logika bisnis di service atau action class, query Eloquent yang bebas N+1, dan test otomatis untuk setiap fitur. Ditambah checklist deploy yang konsisten, kebiasaan ini membuat aplikasi tetap mudah dirawat saat timnya bertambah dan fiturnya membesar. Contoh di bawah berlaku untuk Laravel 12 dengan PHP 8.2 ke atas.

1. Controller Tipis, Logika di Tempat yang Jelas

Controller cukup menerima request, memanggil logika, dan mengembalikan response. Logika bisnis yang dipakai ulang (dari controller, command, atau job) pindahkan ke service atau action class.

class InvoicePaymentController extends Controller
{
    public function store(PayInvoiceRequest $request, Invoice $invoice, PayInvoice $payInvoice): RedirectResponse
    {
        $payInvoice->handle($invoice, $request->integer('amount'));

        return to_route('invoices.show', $invoice)->with('status', 'Pembayaran tercatat.');
    }
}
class PayInvoice
{
    public function handle(Invoice $invoice, int $amount): Payment
    {
        if ($invoice->isPaid()) {
            throw ValidationException::withMessages(['amount' => 'Invoice ini sudah lunas.']);
        }

        return DB::transaction(function () use ($invoice, $amount) {
            $payment = $invoice->payments()->create(['amount' => $amount]);
            $invoice->update(['paid_at' => now()]);

            return $payment;
        });
    }
}

Pola ini membuat logika bisa diuji tanpa HTTP dan dipanggil dari mana saja. Pembahasan pola lain ada di design pattern untuk developer Laravel.

2. Validasi di Form Request

php artisan make:request PayInvoiceRequest
public function rules(): array
{
    return [
        'amount' => ['required', 'integer', 'min:1'],
    ];
}

public function authorize(): bool
{
    return $this->user()->can('pay', $this->route('invoice'));
}

Validasi dan otorisasi di satu tempat membuat controller bersih dan aturan mudah ditemukan.

3. Eloquent yang Efisien

Hindari N+1

// N+1: satu query per invoice untuk mengambil customer
$invoices = Invoice::latest()->paginate(20);

// Benar: dua query saja
$invoices = Invoice::with('customer')->latest()->paginate(20);

Aktifkan pencegahan lazy loading di lingkungan development agar N+1 langsung ketahuan:

// AppServiceProvider::boot()
Model::preventLazyLoading(! app()->isProduction());

Ambil kolom seperlunya dan proses data besar bertahap

Invoice::select(['id', 'number', 'total'])->where('status', 'unpaid')->get();

Invoice::where('status', 'unpaid')->lazy()->each(fn (Invoice $invoice) => ...);

Optimasi di level database, seperti index dan membaca EXPLAIN, dibahas di MySQL performance tuning.

4. Konfigurasi dan Rahasia

  • Panggil env() hanya di file config/. Di kode aplikasi, gunakan config('services.midtrans.key'). Setelah config:cache, env() di luar config mengembalikan null.
  • Jangan commit file .env. Simpan contoh variabel di .env.example.
  • Gunakan enum PHP untuk status dan cast di model, bukan string bebas.
enum InvoiceStatus: string
{
    case Unpaid = 'unpaid';
    case Paid = 'paid';
}

protected function casts(): array
{
    return ['status' => InvoiceStatus::class, 'paid_at' => 'datetime'];
}

5. Pekerjaan Lambat ke Queue

Kirim email, buat PDF, panggil API pihak ketiga, dan proses file besar di queue agar response tetap cepat.

SendInvoiceEmail::dispatch($invoice);

Di production, jalankan worker dengan Supervisor atau layanan sejenis, dan gunakan Redis sebagai driver queue untuk beban yang lebih tinggi. Pemakaian Redis sebagai cache dibahas di Redis cache layer di Laravel.

6. Test dengan Pest

it('menolak pembayaran untuk invoice yang sudah lunas', function () {
    $invoice = Invoice::factory()->paid()->create();

    $this->actingAs($invoice->owner)
        ->post(route('invoices.payments.store', $invoice), ['amount' => $invoice->total])
        ->assertSessionHasErrors('amount');
});

Utamakan feature test yang menguji perilaku dari sisi pengguna. Pakai factory dengan state yang jelas, seperti paid(), agar test mudah dibaca.

7. Gaya Kode Konsisten

  • Jalankan Laravel Pint sebelum commit agar format seragam.
  • Gunakan type hint dan return type di semua method.
  • Pakai perintah php artisan make: untuk membuat file agar struktur folder standar.

Laravel 12 Best Practices Saat Deploy Production

composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize        # cache config, route, view, event
npm ci && npm run build
php artisan queue:restart
  • APP_ENV=production dan APP_DEBUG=false.
  • HTTPS aktif, session dan cookie aman.
  • Backup database terjadwal dan pernah diuji pemulihannya.
  • Log dipantau, dan error dikirim ke layanan pemantauan.
  • Scheduler berjalan lewat cron: * * * * * php artisan schedule:run.

Langkah server lengkapnya ada di deploy Laravel ke VPS Ubuntu.

Kesalahan yang Sering Terjadi

  • Logika bisnis di Blade atau model yang membengkak. Model berisi relasi, cast, dan scope; logika alur pindah ke action atau service.
  • Mass assignment tanpa $fillable yang membuat kolom seperti is_admin bisa diisi dari form.
  • Debug mode menyala di production, yang membocorkan konfigurasi saat terjadi error.
  • Tidak ada test sama sekali, sehingga setiap upgrade versi menjadi taruhan.

Langkah Berikutnya

Pastikan keamanan aplikasi dengan OWASP Top 10 untuk Laravel, lalu siapkan environment yang seragam untuk tim dengan Docker untuk Laravel.