Laravel adalah framework PHP yang paling banyak digunakan di dunia, namun popularitas tidak menjamin semua kode yang ditulis dengan Laravel adalah kode yang baik. Banyak proyek Laravel yang dimulai dengan bersih namun berakhir menjadi "spaghetti code" setelah beberapa bulan pengembangan. Artikel ini merangkum best practices yang saya terapkan di proyek-proyek production untuk menjaga codebase tetap bersih, testable, dan maintainable.
Prinsip Dasar: Fat Model, Skinny Controller
Prinsip lama tapi masih relevan. Controller seharusnya hanya bertugas menerima request, meneruskan ke service/model, dan mengembalikan response. Logika bisnis tidak boleh berada di controller.
// ❌ Buruk — logika bisnis di controller
public function store(Request $request): JsonResponse
{
$user = User::where('email', $request->email)->first();
if ($user && Hash::check($request->password, $user->password)) {
$token = $user->createToken('api')->plainTextToken;
return response()->json(['token' => $token]);
}
return response()->json(['message' => 'Unauthorized'], 401);
}
// ✅ Baik — delegasi ke service
public function store(LoginRequest $request): JsonResponse
{
$token = $this->authService->login($request->validated());
return response()->json(['token' => $token]);
}
Service Layer: Jangan Lewatkan Ini
Laravel tidak memaksakan Service Layer, tapi proyek medium-to-large hampir selalu membutuhkannya. Service class berisi logika bisnis yang bisa digunakan dari controller, queue job, console command, atau test. Letakkan di app/Services/.
class OrderService
{
public function __construct(
private readonly OrderRepository $orders,
private readonly PaymentGateway $payment,
) {}
public function create(User $user, array $items): Order
{
return DB::transaction(function () use ($user, $items) {
$order = $this->orders->create($user, $items);
$this->payment->charge($order);
OrderCreated::dispatch($order);
return $order;
});
}
}
Form Request untuk Validasi
Selalu gunakan Form Request daripada validasi inline di controller. Keuntungannya: validasi bisa di-test terpisah, controller lebih bersih, dan authorization logic terpisah dari request handling.
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'content' => ['required', 'string', 'min:100'],
'status' => ['required', Rule::in(['draft', 'published'])],
];
}
}
Eloquent: Scope dan Query Optimization
Hindari N+1 dengan eager loading secara eksplisit. Jangan mengandalkan lazy loading — aktifkan strict mode di environment development:
// AppServiceProvider
public function boot(): void
{
Model::shouldBeStrict(! app()->isProduction());
}
// Query dengan eager loading
$posts = Post::with(['author', 'categories', 'tags'])
->published()
->latest()
->paginate(15);
// Local scope
public function scopePublished(Builder $query): Builder
{
return $query->where('status', 'published')
->where('published_at', '<=', now());
}
Repository Pattern: Kapan Diperlukan?
Repository pattern di Laravel sering di-over-use. Gunakan hanya jika:
- Anda perlu swap data source (misalnya dari database ke API eksternal)
- Logika query sangat kompleks dan berulang di banyak tempat
- Anda perlu mock database di test tanpa menyentuh database asli
Untuk CRUD sederhana, Eloquent langsung dari Service sudah cukup. Abstraksi berlebihan menambah kompleksitas tanpa manfaat nyata.
Konfigurasi dan Environment
Aturan penting yang sering dilanggar: jangan pernah gunakan env() di luar config files. Alasannya: saat config di-cache (php artisan config:cache), env() akan selalu mengembalikan null.
// ❌ Salah
$key = env('OPENAI_API_KEY');
// ✅ Benar
$key = config('services.openai.key');
// config/services.php
'openai' => [
'key' => env('OPENAI_API_KEY'),
],
Queue untuk Operasi Berat
Semua operasi yang memakan lebih dari ~200ms seharusnya di-queue. Contoh umum: kirim email, proses image, generate PDF, panggil external API. Jangan biarkan pengguna menunggu operasi background di HTTP request.
Deploy ke Production
Checklist deployment Laravel yang minimal harus dilakukan:
php artisan config:cache— cache konfigurasiphp artisan route:cache— cache routingphp artisan view:cache— pre-compile Blade templatesphp artisan migrate --force— jalankan migrasi pending- Pastikan
APP_ENV=productiondanAPP_DEBUG=false - Setup queue worker dengan Supervisor
- Setup scheduler di crontab
Kode yang bersih bukan tentang mengikuti semua pattern yang ada — ini tentang memilih abstraksi yang tepat untuk kompleksitas yang ada. Laravel sudah menyediakan semua tools yang diperlukan; tugas Anda adalah menggunakannya secara konsisten dan proporsional.