0% menganggap dokumen ini bermanfaat (0 suara)
54 tayangan84 halaman

La Ravel Phase 2

Dokumen ini membahas cara membuat aplikasi Laravel sesuai dengan standar best practice, mencakup berbagai aspek seperti penamaan, penerapan prinsip DRY dan SOLID, serta penggunaan fitur-fitur Laravel seperti Queue dan Repository Pattern. Selain itu, dokumen ini juga menjelaskan tentang logging, monitoring, dan optimasi performa aplikasi menggunakan caching. Terdapat juga panduan tentang penggunaan migrations dan seeder, serta penyusunan routing untuk proyek besar.

Diunggah oleh

Bakul Impian
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
54 tayangan84 halaman

La Ravel Phase 2

Dokumen ini membahas cara membuat aplikasi Laravel sesuai dengan standar best practice, mencakup berbagai aspek seperti penamaan, penerapan prinsip DRY dan SOLID, serta penggunaan fitur-fitur Laravel seperti Queue dan Repository Pattern. Selain itu, dokumen ini juga menjelaskan tentang logging, monitoring, dan optimasi performa aplikasi menggunakan caching. Terdapat juga panduan tentang penggunaan migrations dan seeder, serta penyusunan routing untuk proyek besar.

Diunggah oleh

Bakul Impian
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai PDF, TXT atau baca online di Scribd

Belajar Membuat Aplikasi

Laravel Sesuai Standar Best


Practice

Asdita Prasetya - @codingtengahmalam


Table of contents

Pembukaan 5

Hak Cipta & Ketentuan Penggunaan 6

Changelogs 7
v1.2.0 (15 Mei 2025) - Rilis Phase 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
v1.0.0 (12 Maret 2025) - Rilis Awal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Aturan Penamaan Laravel 8

Konsep DRY (Don’t Repeat Yourself) 10


Penerapan DRY dalam Kehidupan Sehari-hari . . . . . . . . . . . . . . . . . . . . . 10
Penerapan DRY dalam Aplikasi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Implementasi Gaya Koding SOLID 13


Single responsibility principle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Open/closed principle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Liskov substitution principle (LSP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Interface segregation principle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

Mutator dan Accessor untuk Format Tanggal 23

Menggunakan Mass Assignment 25

Hindari Penggunaan Magic String 27


Kekurangan Magic String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Cara menghindari Magic String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Kirimkan Tugas Berat Ke Background 31

Pengenalan Laravel Queue 32


Driver Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Implementasi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Buat Background Job . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Cara Kerja Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Menjalankan Queue Worker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

2
Laravel Repository Pattern 37
What is Repository Pattern? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Implementasi Repository Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Buat Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Implementasi Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Implementasi Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

Logging dan Monitoring 42


Apa yang dimaksud Logging dan Montoring? . . . . . . . . . . . . . . . . . . . . . . 42
Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Monitoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Logging Aplikasi Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Rotasi FIle Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Monitoring Aplikasi Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Kesalahan Umum Logging dan Monitoring . . . . . . . . . . . . . . . . . . . . . . . . 46

N+1 Query Problem 48


Studi kasus N+1 Query Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Contoh Masalah . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Solusi utama: Eager Loading . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Perbandingan Jumlah Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Bagaimana Data Comments Dipetakan ke Campaign? . . . . . . . . . . . . . . 52
Fitur Lanjutan Eager Loading di Laravel . . . . . . . . . . . . . . . . . . . . . . . . 52
Eager Loading Multiple Relationships . . . . . . . . . . . . . . . . . . . . . . . 52
Nested Eager Loading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Constraining Eager Loads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Kesimpulan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

Mempercepat Load Data dengan Caching Layer 54


Tanpa Menggunakan Cache Layer . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Menggunakan Cache Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Redis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Kenapa Redis cocok banget buat cache? . . . . . . . . . . . . . . . . . . . . . . . . . 56
Optimasi Query di Aplikasi Donasi Online . . . . . . . . . . . . . . . . . . . . 56
Implementasi Cache Donasi Online . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Hapus cache jika data sudah tidak diperlukan . . . . . . . . . . . . . . . . . . . . . . 59

Menggunakan Migrations dan Seeder secara Maksimal 61


Latar Belakang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Larevel Migrations dan Seeder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Menggunakan Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
Menggunakan Seeder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Membuat Migration dan Seeder yang baik . . . . . . . . . . . . . . . . . . . . 66

3
Menyusun Routing untuk Project Besar 68
Berbagai File Router dan Kegunaan-nya . . . . . . . . . . . . . . . . . . . . . . . . . 68
Maksimalkan router Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Menggunakan named router . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Menggunakan resource router untuk CRUD . . . . . . . . . . . . . . . . 70
Menggunakan route model binding . . . . . . . . . . . . . . . . . . . . . . 71
Memisahkan file router sesuai context . . . . . . . . . . . . . . . . . . . . 72
Jangan gunakan clousure file routing . . . . . . . . . . . . . . . . . . . . . 74

Helper Function Solusi untuk Kode yang Reusable dan Konsisten 75


Pengertian Helper Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Kapan Menggunakan Helper Function? . . . . . . . . . . . . . . . . . . . . . . 75
Jangan Gunakan Helper function jika . . . . . . . . . . . . . . . . . . . . . . . 76
Helper Function Bawaan Laravel . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Custom Helper function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Perbandingan Helper Bawaan vs Custom Helper Function . . . . . . . . . . 78
Best Practice Penggunaan Helper Function . . . . . . . . . . . . . . . . . . . . . . . 79

Implementasi Form Request untuk Validasi Modular 80


Laravel Form Request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Kustomisasi Form Request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Custom error message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Menggubah nama Atribut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

4
Pembukaan

Sebagai developer Laravel, seringkali kita menghadapi beberapa masalah seperti: kode yang
sulit dibaca, performa aplikasi yang menurun seiring pertumbuhan pengguna, struktur kode
yang makin rumit, dan masih banyak lagi.
Masalah ini seringkali disebabkan oleh kurangnya pemahaman terhadap best practice dalam
pengembangan aplikasi Laravel. Maka dari itu saya menulis buku dengan judul “Belajar
Membuat Aplikasi Laravel Sesuai Standar Best Practice” Harapan saya buku ini hadir
untuk membantu mengatasi masalah yang muncul dalam pengembangan aplikasi, selain itu
juga membantu developer Laravel berkembang lebih baik lagi dengan tidak hanya fokus pada
pengembangan aplikasi saja, tetapi juga memperhatikan best practice dalam pengembangan
aplikasi.
Buku ini berisi berbagai best practices yang bisa langsung diterapkan, mulai dari tips pengelo-
laan kode, desain arsitektur, hingga teknik optimalisasi performa dan keamanan
Ebook ini cocok untuk semua tingkatan developer Laravel: dari pemula yang ingin menerapkan
standar yang baik, hingga developer profesional yang ingin menyempurnakan aplikasi berskala
besar.
Sebagai bonus, ebook ini dilengkapi dengan studi kasus proyek nyata: aplikasi donasi online
yang terintegrasi dengan pembayaran otomatis Midtrans. Anda akan dibimbing melalui setiap
langkah, memahami praktik terbaik dalam proyek nyata yang siap produksi.
Dengan panduan ini, Anda bisa meningkatkan keterampilan Laravel dan memastikan aplikasi
yang Anda bangun lebih profesional, aman, dan scalable!

5
Hak Cipta & Ketentuan Penggunaan

© 2025 Asdita Prasetya. Seluruh Hak Cipta Dilindungi Undang-Undang.


Seluruh isi dari eBook ini, termasuk namun tidak terbatas pada teks, gambar, ilustrasi, dan
format penyajian, dilindungi oleh undang-undang hak cipta yang berlaku.
Setiap pelanggaran terhadap hak cipta eBook ini akan dikenakan tindakan hukum sesuai
dengan peraturan yang berlaku. Pemilik hak cipta berhak untuk mengambil langkah hukum,
termasuk tetapi tidak terbatas pada tuntutan perdata dan pidana terhadap pihak yang
melakukan pelanggaran.
Jika Anda menemukan distribusi ilegal dari eBook ini, harap segera laporkan kepada kami
melalui

• Email: codingtengahmalam@gmail.com
• Instagram: (codingtengahmalam?)

6
Changelogs

Berikut adalah daftar perubahan dan pembaruan isi eBook ini dari waktu ke waktu. Kami
berkomitmen untuk terus memperbarui konten sesuai perkembangan Laravel dan best practice
terbaru.

v1.2.0 (15 Mei 2025) - Rilis Phase 2

• Penambahan bab baru dengan tujuan membuat Laravel yang lebih modular dan aman

v1.0.0 (12 Maret 2025) - Rilis Awal

• Versi awal dari eBook ini dirilis dengan fokus pada Fundamental Best Practices Laravel
• Menyediakan panduan lengkap tentang penggunaan Laravel, mulai dari instalasi hingga
penerapan best practices dalam pengembangan aplikasi
• Menyertakan contoh kode dan penjelasan mendetail untuk setiap topik yang dibahas
• Memperkenalkan konsep-konsep dasar Laravel seperti routing, middleware, dan pengelo-
laan database

7
Aturan Penamaan Laravel

Laravel adalah framework yang telah di desain sedermikian rupa untuk membuat developer
menjadi lebih produktif, efektif dan membuat kita hanya fokus untuk mengembangkan produk
saja.
Untuk meningkatkan produktivitas hal yang harus dilakukan adalah menulis kode sesuai den-
gan standar Laravel. baik bekerja secara tim maupun individu, mengikuti standar kode meru-
pakan investasi.
Mungkin sebagian developer yang belum terbiasa akan merasa mengikuti standar malah mem-
persulit proses development. Namun percayalah, ketika project kamu sudah selesai dan suatu
hari nanti kamu perlu membuka-nya kembali jika kamu tidak mengikuti standar yang ada,
kamu akan membuang banyak waktu.
Selain itu, mengikuti konvensi penamaan Laravel juga meningkatkan keamanan dan performa
aplikasi. Dengan struktur yang konsisten, lebih mudah untuk mendeteksi dan memperbaiki
potensi masalah keamanan. Performa juga dapat ditingkatkan karena kode yang terorganisir
dengan baik lebih mudah dioptimalkan dan di-cache.
Saat ini Laravel menerapkan PSR-12: Extended Coding Style standar ini berisikan
ketentuan yang sangat mendetail mengenai gaya untuk menulis kode PHP.
Berikut ini adalah contoh konversi penamaan yang sesuai dengan standar Laravel
Bahasa
Gunakan bahasa Inggris untuk semua komponen dalam Laravel.
Database - Nama Tabel: Gunakan kata jamak (contoh: donations).
- Nama Kolom: Gunakan snake_case (contoh: short_description).
- Primary Key: Gunakan id sebagai primary key secara default.
Model & Controller - Model: Gunakan kata tunggal (contoh: Donation).
- Controller: Gunakan kata tunggal dengan akhiran Controller (contoh: DonationController).
Method, Variable, dan Route - Method: Gunakan camelCase (contoh: donationPackages).
- Variabel: Gunakan camelCase (contoh: $currentDonation).
- Route:
- Gunakan kata jamak (contoh: Route::resource('donations')).
- Gunakan snake_case dengan notasi dot untuk nama router (contoh: donations.show_package).

8
Relationship dalam Eloquent - hasOne, belongsTo: Gunakan kata tunggal (contoh:
author).
- hasMany, belongsToMany: Gunakan kata jamak (contoh: donations, payments).
Seeder & View - Seeder Gunakan kata tunggal dengan akhiran Seeder (contoh:
DonationSeeder).
- View: Gunakan kebab-case untuk penamaan file (contoh: show-detail-donation.blade.php).
Resource Controller - Gunakan Route::resource('donations', DonationController::class)
hanya jika controller digunakan untuk mengelola resource.
Dan masih banyak lagi konvensi penamaan yang perlu diperhatikan dalam Laravel.
Dengan mengikuti konvensi penamaan yang konsisten seperti yang dijelaskan di atas, pro-
jectmu akan menjadi lebih terstruktur dan mudah dipahami. Hal ini tidak hanya membantu
dalam pemeliharaan kode jangka panjang, tetapi juga memudahkan kolaborasi dengan devel-
oper lain.

9
Konsep DRY (Don’t Repeat Yourself)

Konsep DRY (Don’t Repeat Yourself) dalam pemrograman berarti menghindari pengu-
langan kode atau logika yang sama di berbagai tempat. Tujuannya adalah membuat kode
lebih efisien, mudah dipelihara, dan bebas dari bug.
Dengan menerapkan prinsip DRY, kita bisa mengubah atau memperbaiki logika hanya pada
satu tempat saja, tanpa harus mencari dan mengubah kode yang sama di banyak lokasi.

Penerapan DRY dalam Kehidupan Sehari-hari

Konsep DRY juga bisa diterapkan dalam kehidupan sehari-hari untuk meningkatkan efisiensi
dan produktivitas. Salah satu contoh sederhana adalah dalam membuat daftar belanja ming-
guan. Mari kita lihat bagaimana konsep DRY bisa membantu mengorganisir kegiatan rutin
dengan lebih efektif.
Daripada menulis ulang daftar belanja setiap minggu dari awal, kita bisa menyimpan satu
daftar belanja mingguan yang berisi barang-barang kebutuhan rutin, seperti beras, telur, susu,
dan sayuran. Setiap minggu, cukup buka daftar tersebut dan tambahkan atau kurangi barang
sesuai kebutuhan saat itu.

� Daftar Belanja Mingguan

Kebutuhan Pokok
- [ ] Beras – 5 kg
- [ ] Telur – 1 lusin
- [ ] Susu UHT – 3 kotak
- [ ] Minyak goreng – 2 liter
- [ ] Gula pasir – 1 kg

Sayuran & Buah


- [ ] Wortel – 500 gr
- [ ] Bayam – 1 ikat
- [ ] Tomat – 500 gr
- [ ] Pisang – 1 sisir

10
Protein
- [ ] Daging ayam – 1 kg
- [ ] Tahu – 10 potong
- [ ] Tempe – 2 papan

Kebutuhan Rumah Tangga


- [ ] Sabun cuci piring
- [ ] Deterjen
- [ ] Pasta gigi

Keuntungan: KIta tidak perlu mengulang penulisan barang yang sama berulang kali, se-
hingga menghemat waktu dan memastikan tidak ada barang yang terlewat.

Penerapan DRY dalam Aplikasi

Dalam aplikasi penggalangan dana, kita sering membutuhkan data total donasi yang dita-
mpilkan di berbagai tempat seperti Dashboard, Report, detail donasi, dan riwayat donasi
pengguna. Berikut adalah contoh penerapan dengan dan tanpa konsep DRY:
Tanpa menerapkan konsep DRY

// DashboardController
$totalDonations = Donation::sum('amount');

// ReportController
$totalDonations = Donation::sum('amount');

// UserController
$totalDonations = Donation::where('user_id', $userId)->sum('amount');

Dengan menerapkan konsep DRY

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Donation extends Model


{
// Reusable method
public static function getTotalDonations($userId = null)

11
{
$query = self::query();

if ($userId) {
$query->where('user_id', $userId);
}

return $query->sum('amount');
}
}

Maka pada tiap - tiap controller kita cukup memanggil method yang sudah kita buat sebelum-
nya, seperti contoh dibawah ini:

// DashboardController
$totalDonations = Donation::getTotalDonations();

// ReportController
$totalDonations = Donation::getTotalDonations();

// UserController
$totalDonations = Donation::getTotalDonations($userId);

Keuntungan
Dengan menerapkan konsep DRY, kita mendapatkan beberapa keuntungan: 1. Mudah
Dikelola: Perubahan logika hanya perlu dilakukan di satu tempat. 2. Konsisten: Seluruh
bagian aplikasi menggunakan logika yang seragam. 3. Efisien: Menghilangkan pengulangan
kode yang tidak perlu.
Penerapan DRY menjadikan aplikasi lebih modular dan mudah dikembangkan di masa de-
pan.
Selain itu, DRY juga mendorong pengembang untuk berpikir lebih sistematis dalam merancang
solusi, sehingga menghasilkan arsitektur aplikasi yang lebih baik. Dengan menerapkan prinsip
ini secara konsisten, tim pengembang dapat fokus pada fitur-fitur baru daripada menghabiskan
waktu untuk memperbaiki kode yang berulang.

12
Implementasi Gaya Koding SOLID

Salah satu konsep yang sering dipakai dalam pengembangan perangkat lunak adalah SOLID.
Konsep ini pertama kali diperkenalkan oleh Robert C. Martin pada tahun 2000-an lewat
bukunya, “Design Principles and Design Patterns.”. Berikut ini adalah keuntungan jika kita
menggunakan konsep SOLID dalam pengembangan perangkat lunak:

1. Kode lebih mudah dipahami dan dirawat


2. Menghindari conflict saat bekerja dengan team
3. Perubahan kode menjadi lebih aman
4. Kode lebih mudah untuk di test dengan menggunakan unit test

SOLID terdiri dari 4 konsep utama, yaitu Single Responsibility Principle (SRP), Open-Closed
Principle (OCP), Liskov Substitution Principle (LSP), dan Interface Segregation Principle
(ISP). Mari kita bahas satu per satu konsep tersebut.

Single responsibility principle

Setiap class atau fungsi pada kode wajib memiliki satu tanggung jawab dan hanya satu
alasan untuk diubah. Tujuannya adalah meningkatkan kohesi, mengurangi kompleksitas, dan
memudahkan modifikasi. Dengan kata lain, suatu fungsi tidak diperbolehkan mengerjakan
banyak hal,
Sebagai contoh, fungsi yang mendefinisikan struktur data hanya akan diubah saat ada peruba-
han pada struktur data tersebut, sedangkan class/fungsi yang menangani business logic hanya
akan dimodifikasi ketika terjadi perubahan pada business logic.
Di Laravel, kita mengenal Form Request yang, jika diimplementasikan dengan tepat, akan
sesuai dengan konsep SRP. Berikut adalah contoh penerapannya:

// form request

class DonationRequest extends FormRequest


{
public function rules(): array
{
return [

13
'campaign_id' => 'required',
'amount' => 'required|numeric',
'message' => 'nullable|string',
'is_visible' => 'boolean'
];
}
}

// Controller
namespace App\Http\Controllers;

class CampaignController extends Controller


{
public function donateCampaign(DonationRequest $request)
{
$validatedData = $request->validated()
$campaign = Campaign::findOrFail($request->campaign_id);

// logic lainnya
}
}

Pada contoh di atas, proses validasi data dipisahkan dari controller dan ditempatkan pada
class terpisah yaitu DonationRequest. Dengan demikian, controller dapat fokus pada penan-
ganan logika bisnis utama, sedangkan validasi data dikelola secara independen. Pemisahan ini
menghasilkan kode yang lebih terorganisir dan mudah dirawat.
Dengan menerapkan SRP, kita juga mendapatkan keuntungan dalam hal pengujian, karena
setiap komponen dapat diuji secara terpisah dan lebih mudah untuk memastikan bahwa setiap
bagian berfungsi sesuai dengan yang diharapkan. Selain itu, ketika ada kebutuhan untuk
mengubah logika validasi, kita hanya perlu fokus pada DonationRequest tanpa khawatir akan
mempengaruhi logika bisnis di controller.

Open/closed principle

Software entities (kelas, modul, fungsi, dll.) sebaiknya terbuka untuk ekstensi tetapi tertutup
untuk modifikasi, kita dapat menambahkan fitur baru tanpa harus mengubah kode yang sudah
ada.
Hal ini membantu kita menghindari bug yang mungkin muncul akibat modifikasi kode yang
telah berjalan. Memodifikasi kode yang sudah ada berisiko menimbulkan bug di production.
Selain itu, kita harus melakukan testing ulang pada kode lama—sesuatu yang seharusnya tidak

14
perlu jika menerapkan Open-closed Principle. Prinsip ini mendorong kita untuk menulis kode
yang modular.
Contoh kasus penerapan konsep OCP, perhatikan kode dibawah ini

public function donateCampaign(DonationRequest $request)


{
$validatedData = $request->validated()
$campaign = Campaign::findOrFail($request->campaign_id);

$donation = Donation::create([
'campaign_id' => $campaign->id,
'user_id' => auth()->id(),
'amount' => $request->amount,
'message' => $request->message,
'is_visible' => $request->is_visible,
'campaign_detail' => json_encode($campaign),
'status' => Donation::STATUS_PENDING,
]);

$midtransSnapClient = new MidtransSnap($donation);


$snapLink = $midtransSnapClient->getSnapLink();

$donation->update([
'payment_link' => $snapLink->redirect_url,
'payment_detail' => json_encode($snapLink)
]);

return response()->json([
'redirect_url' => $snapLink->redirect_url
]);
}

Kode di atas menangani donasi untuk campaign dengan pembayaran melalui Midtrans. Na-
mun, kode ini tidak mengikuti Open/Closed Principle (OCP) karena hanya mendukung
satu metode pembayaran. Ketika kita ingin menambahkan metode pembayaran baru seperti
PayPal atau Manual Payment, kita terpaksa mengubah kode yang sudah ada.
Penerapan OCP
Agar lebih fleksibel, dan mengikuti konsep OCP, kita bisa memisahkan logika pembayaran
menggunakan Polymorphism dan Dependency Injection.
Buat Interface untuk Pembayaran

15
interface PaymentGatewayInterface {
public function getPaymentLink(Donation $donation);
}

Implementasi sesuai dengan metode pembayaran

// Midtrans payment
class MidtransGateway implements PaymentGatewayInterface {
public function getPaymentLink(Donation $donation) {
$midtransSnapClient = new MidtransSnap($donation);
return $midtransSnapClient->getSnapLink()->redirect_url;
}
}

// Paypal payment
class PaypalGateway implements PaymentGatewayInterface {
public function getPaymentLink(Donation $donation) {
// integrasi paypay
return "https://paypal.com/checkout?...."
}
}

// handle manual payment


class ManualPaymentGateway implements PaymentGatewayInterface {
public function getPaymentLink(Donation $donation) {
// integrasi paypay
return "https://beramal.com/payment?...."
}
}

Ubah fungsi donateCampaign untuk mendukung multiple payment gateway

public function donateCampaign(DonationRequest $request)


{
$validatedData = $request->validated()

$campaign = Campaign::findOrFail($request->campaign_id);

$donation = Donation::create([
'campaign_id' => $campaign->id,
'user_id' => auth()->id(),

16
'amount' => $request->amount,
'message' => $request->message,
'is_visible' => $request->is_visible,
'campaign_detail' => json_encode($campaign),
'status' => Donation::STATUS_PENDING,
]);

// Pilih gateway pembayaran berdasarkan request


$paymentMethod = match ($request->payment_method) {
'midtrans' => new MidtransGateway(),
'paypal' => new PaypalGateway(),
'manual' => new ManualPaymentGateway (),
default => throw new \Exception("Metode pembayaran tidak didukung")
};

// Dapatkan link pembayaran dari gateway yang dipilih


$paymentLink = $paymentMethod->getPaymentLink($donation);

$donation->update([
'payment_link' => $paymentLink,
'payment_detail' => json_encode(['redirect_url' => $paymentLink ])
]);

return response()->json([
'redirect_url' => $paymentLink
]);
}

Dengan implementasi ini, fungsi donateCampaign telah menerapkan konsep Open/closed prin-
ciple dengan baik. Ketika ada kebutuhan menambah payment gateway baru di masa depan,
kita hanya perlu membuat class baru yang mengimplementasikan interface yang sudah ada
dan menambahkan payment method-nya.

Liskov substitution principle (LSP)

Konsep ini berkaitan dengan inheritance dalam pemrograman. LSP menyatakan bahwa ketika
suatu class mewarisi class induk (baik interface maupun class lainnya), class turunan tersebut
harus dapat berfungsi dengan benar tanpa mengubah perilaku yang sudah ada. Mari kita
lihat contoh implementasi LSP berikut.

17
Sebagai contoh, kita memiliki interface PaymentGatewayInterface yang telah kita buat se-
belumnya. Setiap class yang mengimplementasikan interface ini (MidtransGateway, Paypal-
Gateway, dan ManualPaymentGateway) dapat saling menggantikan tanpa mengganggu fung-
sionalitas sistem.
Namun kemudian kita akan menambahkan metode “Donasi Gratis”, dimana donasi tidak
memerlukan pembayaran sehingga tidak mengembalikan link pembayaran, contohnya seperti
dibawah ini

class FreeDonationPayment implements PaymentGatewayInterface {


public function getPaymentLink(Donation $donation) {
// Tidak ada pembayaran, langsung set status sukses
$donation->update(['status' => Donation::STATUS_SUCCESS]);
return null; // Tidak ada URL pembayaran!
}
}

Kode di atas berpotensi merusak alur pembayaran donasi karena FreeDonationPayment


mengembalikan nilai null
Penerapan LSP
Ketika melakukan pembayaran dengan metode free payment user tidak membutuhkan link
unuk menuju ke halaman pembayaran. Karena metode pembayarannya yang berbeda maka
tidak bisa untuk menggunakan PaymentGatewayInterface , solusinya adalah dengan mem-
buat interface baru, sehingga kita memiliki interface

1. Pembayaran Online PaymentGatewayInterface


2. Pembayaran Gratis DirectConfirmationPaymentInterface

Kurang lebih isi dari interface-nya sebagai berikut

// interface
interface PaymentGatewayInterface {
public function getPaymentLink(Donation $donation);
}

interface DirectConfirmationPaymentInterface {
public function confirmPayment(Donation $donation);
}

// buat class sesuai dengan interface


class MidtransGateway implements PaymentGatewayInterface {
public function getPaymentLink(Donation $donation) {

18
return "https://midtrans.com/pay/{$donation->id}";
}
}

class PayPalGateway implements PaymentGatewayInterface {


public function getPaymentLink(Donation $donation) {
return "https://paypal.com/checkout?donation_id={$donation->id}";
}
}

class FreeDonationPayment implements DirectConfirmationPaymentInterface {


public function confirmPayment(Donation $donation) {
$donation->update(['status' => Donation::STATUS_SUCCESS]);
return "Donasi berhasil dikonfirmasi tanpa pembayaran.";
}
}

Maka kita akan menyesuaikan fungsi donateCampaign menjadi seperti dibawah ini

$paymentMethods = [
'midtrans' => new MidtransPayment(),
'paypal' => new PayPalPayment(),
'free_donation' => new FreeDonationPayment(),
];

if (!isset($paymentMethods[$request->payment_method])) {
throw new \Exception("Metode pembayaran tidak didukung");
}

$paymentMethod = $paymentMethods[$request->payment_method];

if ($paymentMethod instanceof PaymentGatewayInterface) {


$snapLink = $paymentMethod->getPaymentLink($donation);
$donation->update(['payment_link' => $snapLink]);

return response()->json(['redirect_url' => $snapLink]);


}

if ($paymentMethod instanceof DirectConfirmationPaymentInterface) {


$message = $paymentMethod->confirmPayment($donation);
return response()->json(['message' => $message]);
}

19
// ...

Dengen begini tidak ada error saat menganti metode pembayaran karena FreeDonation tidak
lagi dipaksa menggunakan sistem yang salah, selain itu kita bisa menambah metode pemba-
yaran lain tanpa mengubah kode utama.

Interface segregation principle

Dalam bahasa inggris segregation memiliki arti keeping things separated. jika dikaitkan dengan
Interface segregation principle memiliki arti pemisahan interface.
Dalam pembuatan interface lebih baik membuat banyak interface dengan fungsi yang spesifik,
hal ini lebih baik daripada membuat satu interface dengan fungsi yang tidak spesifik. Tujuan
dari pemisahan interface adalah untuk tidak memaksa client menggunakan kode yang tidak
diperlukan.
Misalnya, kita punya satu interface besar untuk semua jenis pembayaran:

interface PaymentGatewayInterface {
public function getPaymentLink(Donation $donation);
public function refund(Donation $donation);
}

// implementasi

class MidtransPayment implements PaymentGatewayInterface {


public function getPaymentLink(Donation $donation) {
return "https://midtrans.com/pay/{$donation->id}";
}

public function refund(Donation $donation) {


return "Refund diproses melalui Midtrans";
}
}

class FreeDonationPayment implements PaymentGatewayInterface {


public function getPaymentLink(Donation $donation) {
// Tidak butuh link pembayaran
return null;
}

20
public function refund(Donation $donation) {
// Tidak mungkin ada refund untuk donasi gratis
}
}

Pada FreeDonationPayment dipaksa untuk menggunakan PaymentGatewayInterface dan


mengimplemenrasikan metode refund padahal tidak ada refund untuk donasi gratis, hal ini
melanggar Interface Segregation Principle karena ada fungsi yang tidak diperlukan oleh semua
class dalam interface
Lalu solusi yang benar adalah dengan memisahkan interface menjadi lebih kecil, seperti berikut
ini

interface PaymentGatewayInterface {
public function getPaymentLink(Donation $donation);
}

interface RefundablePaymentInterface {
public function refund(Donation $donation);
}

interface DirectConfirmationPaymentInterface {
public function confirmPayment(Donation $donation);
}

// lalu dalam implementasinya

class MidtransPayment implements PaymentGatewayInterface, RefundablePaymentInterface {


public function getPaymentLink(Donation $donation) {
return "https://midtrans.com/pay/{$donation->id}";
}

public function refund(Donation $donation) {


return "Refund diproses melalui Midtrans untuk donasi ID: {$donation->id}";
}
}

class PayPalPayment implements PaymentGatewayInterface, RefundablePaymentInterface {


public function getPaymentLink(Donation $donation) {
return "https://paypal.com/checkout?donation_id={$donation->id}";
}

21
public function refund(Donation $donation) {
return "Refund diproses melalui PayPal untuk donasi ID: {$donation->id}";
}
}

class FreeDonationPayment implements DirectConfirmationPaymentInterface {


public function confirmPayment(Donation $donation) {
$donation->update(['status' => Donation::STATUS_SUCCESS]);
return "Donasi berhasil dikonfirmasi tanpa pembayaran.";
}
}

Sekarang kita telah menerapkan Interface Segregation Principle (ISP) dengan baik, dengan
detail sebagai berikut:

1. MidtransPayment dan PayPalPayment hanya mengimplementasikan metode yang dibu-


tuhkan.
2. FreeDonationPayment tidak perlu mengimplementasikan fungsi getPaymentLink() dan
refund() yang tidak relevan.
3. Kode menjadi lebih bersih, modular, dan mudah dikembangkan.
4. Penambahan metode pembayaran baru bisa dilakukan tanpa mengubah kode utama.

22
Mutator dan Accessor untuk Format Tanggal

Seringkali kita menyimpan data waktu di dalam database, misal kita punya tabel donations,
akan ada kolom payment_at, confirmed_at dan yang lainnya. baiknya dalam menyimpan
data waktu kita hanya perlu untuk menyimpan data tersebut sesuai dengan standar waktu
yang ada dalam laravel.
Standar waktu ini biasanya menggunakan format ISO 8601 (YYYY-MM-DD HH:MM:SS). Den-
gan menyimpan data dalam format standar, kita dapat dengan mudah memanipulasi dan
menampilkan tanggal sesuai kebutuhan menggunakan fitur Accessors dan Mutators di Laravel.
Ini memungkinkan kita untuk mengubah format tanggal saat mengambil atau menyimpan data
tanpa mengubah struktur database.
Berikut adalah contoh penggunaan Accessors dan Mutators untuk mengelola format tanggal:

// Model
protected $casts = [
'payment_at' => 'datetime',
];

/**
* Accessor: Formatkan kolom payment_at saat membaca
* (jika ingin dibaca dalam ISO 8601)
*/
public function getPaymentAtAttribute($value)
{
return Carbon::parse($value)->toIso8601String();
}

/**
* Mutator: Ubah nilai payment_at menjadi format ISO 8601 sebelum disimpan
*/
public function setPaymentAtAttribute($value)
{
$this->attributes['payment_at'] = Carbon::parse($value)->toIso8601String();
}

// View

23
{{ $object->payment_at->toDateString() }}
{{ $object->payment_at }}

Dengan menggunakan Accessors dan Mutators, kita dapat dengan mudah memanipulasi for-
mat tanggal tanpa mengubah data asli di database. Ini memberikan fleksibilitas dalam
menampilkan tanggal sesuai kebutuhan aplikasi, seperti format yang berbeda untuk tampi-
lan user dan format standar untuk penyimpanan atau perhitungan.

24
Menggunakan Mass Assignment

Mass Assignment memungkinkan kita mengisi beberapa atribut model sekaligus dalam satu
langkah dengan menggunakan array.
Metode ini jauh lebih efisien daripada mengisi atribut satu per satu.
Dengan Mass Assignment, kita dapat mengirim array data ke model lalu Laravel akan secara
otomatis menetapkan nilai-nilai tersebut ke atribut yang sesuai. Ini sangat berguna ketika
bekerja dengan form input yang memiliki banyak field. Namun, penting untuk berhati-hati
dan menggunakan fitur ini dengan bijak untuk menghindari masalah keamanan.
Untuk menggunakan Mass Assignment dengan aman, Laravel menyediakan $fillable dan
$guarded pada model.

1. $fillable mendefinisikan atribut yang diizinkan untuk diisi secara massal,


2. $guarded mendefinisikan atribut yang tidak boleh diisi secara massal.

Dengan mengonfigurasi salah satu dari keduanya, kita dapat mencegah pengisian atribut
yang tidak diinginkan dan meningkatkan keamanan aplikasi. Selain itu, selalu validasi in-
put pengguna sebelum menggunakannya dalam Mass Assignment untuk menambah lapisan
keamanan.
Berikut ini adalah contoh dalam menggunakan dan tanpa menggunakan

// Definisikan pada model


protected $fillable = ['name', 'email', 'password'];

// Dengan Mass Assignment


User::create([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => bcrypt('secret'),
]);

// Tanpa Mass Assignment


$user = new User();
$user->name = 'John Doe';
$user->email = 'john@example.com';

25
$user->password = bcrypt('secret');
$user->save();

Selain itu kita juga bisa melakukan mengkombinasikan-nya dengan mengembalikan hasil dari
validasi request, seperti contoh dibawah ini

$validatedData = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8',
]);

User::create([
...$validatedData,
'password' => bcrypt($validatedData['password']),
]);

Dengan menggunakan Mass Assignment yang dikombinasikan dengan validasi request, kita
dapat memastikan data yang masuk sudah tervalidasi sebelum disimpan ke database. Hal ini
tidak hanya membuat kode lebih ringkas, tetapi juga meningkatkan
keamanan dan maintainability aplikasi. Pendekatan ini sangat direkomendasikan untuk
menangani input form dalam aplikasi Laravel.

26
Hindari Penggunaan Magic String

Magic string adalah penulisan nilai string secara langsung dalam kode program tanpa mendefin-
isikannya di satu tempat terpusat.
Praktik ini merupakan pendekatan yang tidak disarankan dalam pengembangan perangkat
lunak karena dapat menyebabkan berbagai masalah dalam pemeliharaan dan pengembangan
kode. Ketika nilai string ditulis secara langsung di berbagai tempat dalam kode, hal ini dapat
membuat kode menjadi sulit untuk dikelola dan rentan terhadap kesalahan. Contoh magic
string sebagai berikut:

class User
{
private string $type;

public function __construct(string $type)


{
$this->type = $type;
}

public function isNormal(): bool


{
return $this->type === 'normal'; // Magic string �
}
}

Kekurangan Magic String

Penggunaan magic string baiknya dihindari, karena memiliki banyak kekurangan, diantaranya
adalah sebagai berikut:
Rentan terhadap typo
String yang di-hardcode tidak memiliki validasi dari compiler atau IDE. Jika typo kesalahan
ini hanya akan terdeteksi saat runtime, yang bisa menyebabkan bug yang sulit dilacak.
Sulit di pelihara (Maintainability)

27
Jika string digunakan pada banyak tempat, maka ketika ada perubahan pada string tersebut
memerluklan perubahan juga pada semua tempat yang menggunakannya, dengan begitu dapat
meningkatkan resiko kesalahan dan membutuhkan waktu untuk maintain hal tesebut.
Contohnya terdapat validasi pada variable $status, yang sebelumnya adalah ‘normal’
menjadi ‘regular’ maka harus mencari dan menggubah semua string “normal” menjadi
“regular”
Sulit dipahami tool Editor
Dengan menggunakan magic string, Tool Editor code seperti PHPStrom, VScode, dan lainnya
sulti untuk memahami project yang sedang dibuka, hal ini menjadikan tidak bisa menjalankan
feature refactoring, automcomplate dan pengecekan tipe tipe data
Konteks tidak Jelas
String yang ditulis hardcode tidak memberkan konteks yang jelas, pembaca kode harus berfikir
dan menebak apa maksud dari string tersebut.
Contohnya jika kita diberikan “nomal”,”regular”,”ordinary” atau “expected” tanpa ada kon-
teks yang jelas hal ini sangat membingungkan

Cara menghindari Magic String

Untuk menghindari penggunaan magic string, kita dapat menggunakan beberapa pendekatan
berikut:
Menggunakan Konstanta
Definisikan string yang sering digunakan sebagai konstanta dalam class:

class User
{
private const TYPE_NORMAL = 'normal';
private string $type;

public function __construct(string $type)


{
$this->type = $type;
}

public function isNormal(): bool


{
return $this->type === self::TYPE_NORMAL; // �

28
}
}

Menggunakan Enum
Gunakan enum untuk membatasi nilai yang valid:

enum UserType: string


{
case NORMAL = 'normal';
case PREMIUM = 'premium';
case ADMIN = 'admin';
}

class User
{
private UserType $type;

public function __construct(UserType $type)


{
$this->type = $type;
}

public function isNormal(): bool


{
return $this->type === UserType::NORMAL; // �
}
}

Class Khusus untuk Konstanta


Buat class terpisah untuk mengelompokkan konstanta terkait:

final class UserTypes


{
public const NORMAL = 'normal';
public const PREMIUM = 'premium';
public const ADMIN = 'admin';
}

class User
{

29
private string $type;

public function __construct(string $type)


{
$this->type = $type;
}

public function isNormal(): bool


{
return $this->type === UserTypes::NORMAL; // �
}
}

Dengan menggunakan pendekatan-pendekatan yang telah dijelaskan di atas untuk menghin-


dari magic string, kita dapat memperoleh beberapa keuntungan signifikan dalam pengemban-
gan perangkat lunak:

• IDE dapat memberikan autocompletion yang akurat dan cepat, membantu developer
menulis kode dengan lebih efisien dan mengurangi kesalahan pengetikan
• Compiler memiliki kemampuan untuk mendeteksi kesalahan pengetikan secara otomatis
pada tahap kompilasi, sehingga mencegah bug sebelum kode dijalankan
• Proses refactoring menjadi jauh lebih mudah dan aman karena semua nilai string terpusat
di satu lokasi, memungkinkan perubahan dapat dilakukan secara konsisten di seluruh
aplikasi
• Kode menjadi lebih self-documenting dan mudah dipahami oleh developer lain, karena
penggunaan konstanta dan enum memberikan konteks yang jelas tentang maksud dan
tujuan dari setiap nilai

30
Kirimkan Tugas Berat Ke Background

31
Pengenalan Laravel Queue

Laravel Queue memungkinkan kita untuk mengirimkan eksekusi tugas yang berat ke back-
ground process, sehingga aplikasi tetap berjalan lancar sambil memproses pekerjaan secara
asinkron.

Ĺ Note

Background process adalah proses yang berjalan di belakang layar tanpa mengganggu
eksekusi utama aplikasi.

Dalam pengembangan aplikasi Laravel skala besar, kita sering membuat fitur yang mem-
butuhkan waktu pemrosesan yang lama, misalnya pengiriman email, pemrosesan file, atau
integrasi dengan layanan pihak ketiga. Jika dijalankan secara langsung, operasi-operasi ini
bisa membuat aplikasi kita terasa lambat bagi pengguna.
Jika kita memiliki fitur untuk melakukan pembayaran lalu mengirimkan email mengenai status
pembayaran. Tanpa queue, proses ini akan membuat server harus menunggu proses pengiriman
email hingga selesai. Dengan queue, kita bisa memindahkan proses kirim email ke background,
dibawah ini adalah ilustrasi dari penggunaan Queue.

Figure 1: Laravel Queue

Jadi dari sisi user akan mendapatkan respon yang cepat karena server tidak perlu menunggu
proses pengiriman email selesai. Queue akan menangani tugas pengiriman email di background
sementara user bisa melanjutkan aktivitas lainnya di aplikasi.

32
Driver Queue

Laravel mendukung beberapa driver untuk menjalankan queue diantaranya adalah database,
Redis, Amazon SQS, dan Beanstalkd. Masing-masing driver memiliki kelebihan dan karakter-
istik yang berbeda.
Database adalah pilihan paling sederhana karena tidak memerlukan service tambahan, Redis
menawarkan performa tinggi dengan in-memory processing, sementara Amazon SQS cocok
untuk aplikasi yang berjalan di AWS.

Implementasi

Contoh kali ini ada pada fitur pembayaran donasi, dimana setelah user melakukan pemba-
yaran, sistem akan mengirimkan email notifikasi dan mengupdate status pembayaran. Untuk
mengoptimalkan proses ini, kita akan menggunakan queue untuk menangani pengiriman email
di background. Dengan begitu, user bisa langsung mendapatkan respons sukses pembayaran
tanpa harus menunggu email terkirim.

Buat Background Job

Untuk membuat job baru di Laravel, kita bisa menggunakan perintah artisan:

php artisan make:job SendPaymentNotification

Perintah ini akan membuat class job baru di folder app/Jobs. Job class ini akan berisi logika
untuk mengirim email notifikasi pembayaran.
Setelah job dibuat, kita perlu mengimplementasikan logika pengiriman email di dalam method
handle(). Method ini akan dijalankan secara otomatis ketika job diproses oleh queue worker.
Berikut adalah contoh implementasi job untuk mengirim email notifikasi pembayaran:

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendPaymentNotification implements ShouldQueue


{
use InteractsWithQueue, Queueable, SerializesModels;

33
protected $payment;

public function __construct($payment)


{
$this->payment = $payment;
}

public function handle()


{
// Logika pengiriman email
Mail::to($this->payment->user->email)
->send(new PaymentNotification($this->payment));
}
}

Pada kode diatas kita melakukan implementasi dari berbagai macam fungsi yang ada dalam
laravel, diantaranya adalah
ShouldQueue
ShouldQueue adalah interface yang menandakan bahwa job ini harus dijalankan dalam
queue.
InteractsWithQueue
InteractsWithQueue adalah trait yang menyediakan metode untuk berinteraksi dengan an-
trian, seperti:

• Menghapus job dari queue


• Error Handling job
• Menunda eksekusi ulang job

Queueable
Queueable adalah trait untuk membuat antrian yang fleksibel seperti:

• Menentukan suatu job akan di eksekusi pada queue yang mana


• Menentukan koneksi queue, dengan ini kita bisa menentukan sartu job dijalankan dengan
driver yang berbeda dengan lainnya
• Menentukan waktu delay sebelum job dieksekusi

Setelah membuat job class, kita bisa menjalankan job tersebut dari controller atau bagian lain
aplikasi dengan cara:

34
SendPaymentNotification::dispatch($payment);

Secara otomatis, proses ini akan berjalan di background sehingga tidak menghambat respons
aplikasi ke user. Diagram di bawah ini mengilustrasikan bagaimana flow queue bekerja dalam
memproses job pengiriman email notifikasi pembayaran.

Cara Kerja Queue

Untuk mempermudah memahami proses penggunaan Queue anda dapat melihat diagram
dibawah ini.

Figure 2: Cara Kerja Queue

Dari diagram tersebut, kita dapat melihat bahwa ketika sebuah job dikirim ke queue, queue
worker akan memproses job tersebut secara asinkron. Job akan diambil dari queue satu per

35
satu, dieksekusi, dan hasilnya akan dicatat. Jika terjadi kegagalan, job dapat diulang sesuai
dengan konfigurasi yang telah ditentukan.

Menjalankan Queue Worker

Untuk memproses job dalam queue, kita perlu menjalankan queue worker. Queue worker
adalah proses yang berjalan di background dan terus memonitor queue untuk mengeksekusi
job yang masuk. Untuk menjalankan queue worker, gunakan perintah:

php artisan queue:work

Di production, disarankan menggunakan process manager seperti Supervisor untuk memas-


tikan queue worker tetap berjalan. Berikut contoh konfigurasi Supervisor:

Ĺ Note

Supervisor adalah proses manajer yang digunakan untuk menjaga queue worker tetap
berjalan secara otomatis.

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/path/to/worker.log

Dengan menggunakan queue aplikasi yang kita buat bisa lebih responsif memberikan response
yang diberikan kepada user. Proses-proses berat seperti pengiriman email atau pemrosesan
file dapat berjalan di background tanpa mengganggu pengalaman pengguna. Hal ini sangat
penting terutama untuk aplikasi yang memiliki traffic tinggi.

36
Laravel Repository Pattern

Design patternadalah suatu metode yang digunakan untuk menyelesaikan permasalahan yang
sering terjadi dan biasanya memiliki suatu pola dalam menyelesaikan masalah.Design Pat-
terndapat mempercepat pengembangan suatu perangkat lunak. Salah satu dariDesign Pat-
ternyang paling sering digunakan adalahRepository Pattern.

What is Repository Pattern?

Secara singkatnya Repository Pattern adalah suatu pendekatan arsitektur perangkat lunak
yang memisahkan antara Business Logic layer dengan Data Access Logic layer. Pemisahan ini
bertujuan untuk membuat kode lebih terstruktur, mudah dikelola, dan mudah diuji.
Dengan menggunakan repository pattern, business logic layer tidak perlu mengetahui detail
implementasi tentang sumber dan tujuan data. Ini berarti lapisan bisnis tidak perlu tahu
apakah data berasal dari database SQL, NoSQL, API eksternal, atau sistem penyimpanan
lainnya. Business logic layer hanya bertugas mengimplementasikan proses bisnis dan menye-
lesaikan masalah yang ada sesuai dengan kebutuhan aplikasi.
Pendekatan ini memberikan beberapa keuntungan penting: meningkatkan maintainability
kode, memudahkan unit testing, dan membuat kode lebih fleksibel terhadap perubahan sum-
ber data di masa depan. Selain itu, dengan memisahkan kedua lapisan tersebut, developer
dapat bekerja secara paralel pada masing-masing lapisan tanpa saling mengganggu.

37
Figure 1: ilustrasi penggunaan repository pattern

Dalam implementasinya, Repository Pattern biasanya terdiri dari interface yang mendefin-
isikan operasi-operasi dasar untuk mengakses data (seperti Create, Read, Update, Delete)
dan satu atau lebih implementasi konkret dari interface tersebut. Hal ini memungkinkan
untuk mengganti implementasi repository tanpa mempengaruhi kode yang menggunakannya.
Dengan pendekatan ini, kita dapat dengan mudah mengganti sumber data atau menambahkan
fungsionalitas baru tanpa perlu mengubah logika bisnis yang ada.

Implementasi Repository Pattern

Pada contoh implementasi kali ini kita akan menggunakan tabel campaigns yang isinya adalah
informasi iklan donasi yang ada pada sistem donasi online.
Buat Folder Khusus
Langkah pertama adalah kita membuat folder Repository folder ini akan menyimpan berba-
gai repository yang akan digunakan, folder ini berada didalam folder app, untuk lebih jelas
perhatikan tree folder berikut ini

app
����Console
����Exceptions
����Http
� ����Controllers
� ����Middleware

38
����Models
����Providers
����Repository --> Folder untuk menyimpan Repository
� ����Campaign
����Utils

Buat Interface

Untuk memisahkan kode menjadi komponen yang independen (code-decoupling), kita perlu
membuat sebuah interface. Interface ini akan diimplementasikan oleh kelas-kelas yang mem-
butuhkan repository pattern. Interface menjadi kontrak bagi child class-nya yang memberikan
keleluasaan dalam pemilihan mekanisme Data Access Logic.

<?php

namespace App\Repository\Campaign;

use Illuminate\Database\Eloquent\Collection;

interface CampaignRepositoryInterface
{
public function getAll();
public function findById(int $id);
public function create(array $data);
public function update(int $id, array $data);
public function delete(int $id): bool;
}

Jika menggunakan Eloquent, kita dapat membuat kelas EloquentCampaignRepositoy


sebagai child dari CampaignRepositoryInterface. Atau jika menggunakan Elasticsearch,
kita dapat membuat class “ ElasticsearchCampaignRepository sebagai child* dari
CampaignRepositoryInterface.

Implementasi Interface

Setelah membuat interface, langkah selanjutnya adalah mengimplementasikan interface terse-


but ke dalam sebuah kelas konkret. Dalam contoh ini, kita akan membuat implementasi meng-
gunakan Eloquent ORM Laravel. Perhatikan kode berikut yang menunjukkan implementasi
dari interface CampaignRepositoryInterface

39
<?php

namespace App\Repository\Campaign;

class CampaignElaqunetRepository implements CampaignRepositoryInterface


{

public function getAll()


{
return Campaign::all();
}

public function findById(int $id)


{
return Campaign::findOrFail($id);
}

public function create(array $data)


{
return Campaign::create($data);
}

public function update(int $id, array $data)


{
$campaign = Campaign::findOrFail($id);
$campaign->update($data);
return $campaign;
}

public function delete(int $id): bool


{
$campaign = Campaign::findOrFail($id);
return $campaign->delete();
}
}

Implementasi Controller

Setelah membuat repository dan interface-nya, langkah selanjutnya adalah mengimplemen-


tasikan repository tersebut di dalam controller. Berikut adalah contoh implementasi reposi-
tory pattern di dalam controller:

40
<?php

namespace App\Http\Controllers;

use App\Repository\Campaign\CampaignRepositoryInterface;

class CampaignController extends Controller


{
private $campaignRepository;

public function __construct(CampaignRepositoryInterface $campaignRepository)


{
$this->campaignRepository = $campaignRepository;
}

public function index()


{
$campaigns = $this->campaignRepository->getAll();
return view('campaigns.index', compact('campaigns'));
}

public function show($id)


{
$campaign = $this->campaignRepository->findById($id);
return view('campaigns.show', compact('campaign'));
}
}

Dengan menggunakan dependency injection, controller tidak perlu mengetahui implementasi


detail dari repository yang digunakan. Controller hanya perlu mengenal interface-nya saja,
sehingga memungkinkan penggantian implementasi repository tanpa perlu mengubah kode
pada controller.
Maka, dapat disimpulkan bahwa dengan menggunakan Repository Pattern, kita dapat
memisahkan antara business logic dan data access logic dengan lebih baik. Repository
Pattern mengabstraksi data access logic sehingga lebih mudah dikelola. Selain itu, pemisahan
antara business logic dan data access logic juga mempermudah pengujian unit (unit testing)
karena memungkinkan pembuatan mock objects untuk repository. Implementasi Repository
Pattern membantu pengembangan perangkat lunak agar lebih sesuai dengan prinsip-prinsip
SOLID. Dengan demikian, penggunaan Repository Pattern tidak hanya menyederhanakan
tetapi juga memperlancar proses pengembangan perangkat lunak.

41
Logging dan Monitoring

Bagi developer pemula logging dan monitoring seringkali di lupakan, padahal hal ini meru-
pakan suatu aspek yang penting. dengan ini kita bisa melakukan berbagai tindakan opera-
sional yang bisa membuat aplikasi berjalan sesuai dengan fungsinya.
Aplikasi yang telah kita deploy ke production menunjukan penurunan performa, kasusnya
adalah ketika mengambil data perlu loading time yang tinggi.
Maka ketika ada masalah tersebut kita bisa melakukan analisis log dan memerika apa yang
sedang terjadi karena akan ada banyak kemungkinan yang menyebabkan aplikasi kita men-
galami penurunan performa, dari hasil logging dan monitoring yang telah dilakukan kita bisa
menentukan aksi selanjutnya yang lebih cepat dan terarah.

Apa yang dimaksud Logging dan Montoring?

Logging

Proses merekam aktivitas yang terjadi dalam suatu aplikasi dan menyimpannya ke dalam
bentuk file atau sistem penyimpanan lainnya, biasanya disimpan dalam bentuk JSON atau
dalam bentuk file .log lengkap dengan timestamp kejadian. Logging dapat digunakan untuk
melakukan investigasi jika terjadi anomali pada aplikasi yang sedang berjalan.
Pada Laravel secara default semua log akan ditulis pada /project/storage/logs/laravel.log
aktifitas yang di tulis adalah adalah ketika terjadi internal server error dan memberikan stack
trace error tersebut. contohnya seperrti dibawa ini:

[2025-01-29 05:11:09] local.ERROR: SQLSTATE[08006] [7] connection to server at "127.0.0.1", p


password authentication failed for user "default" (Connection: pgsql, SQL: select c.relname a
space and n.nspname not in ('pg_catalog', 'information_schema') order by c.relname) at
/home/hellodit/project/php/beramal/vendor/laravel/framework/src/Illuminate/Database/Connectio
[stacktrace]

#0 project/php/beramal/vendor/laravel/framework/src/Illuminate/Database/Connection.php(767):
Illuminate\\Database\\Connection-> .... amework/src/Illuminate/Database/Connection.php(385):
Illuminate\\Database\\Connection->select()
#\

42
Informasi tersebut dapat kita gunakan untuk melakukan tindakan lanjutan seperti melakukan
bugfixing.

Monitoring

Proses mengamati kondisi aplikasi yang berjalan secara realtime atau berkala, untuk memas-
tikan aplikasi berjalan normal dan mendetaksi secara dini jika terjadi anomali. Proses mon-
itoring biasanya menampilkan data dalam bentuk metrik, grafik atau dashboard contohnya
seperti dibawah ini:

Figure 1: Dashboard Monitoring

Logging Aplikasi Laravel

Laravel menyediakan berbagai fitur logging yang dapat digunakan untuk mencatat aktivitas
aplikasi. Secara default, Laravel menggunakan library Monolog yang menyediakan berbagai
channel logging seperti single, daily, slack, dan lainnya. Konfigurasi logging dapat diatur
melalui file config/logging.php.

43
Kita dapat mengatur level logging sesuai kebutuhan seperti debug, info, warning, error, critical,
dan alert. Setiap level memiliki tingkat kepentingan yang berbeda dan dapat dikonfigurasi
untuk diarahkan ke channel yang berbeda. Sebagai contoh, kita bisa mengirim error logs ke
Slack untuk notifikasi tim, sementara debug logs disimpan dalam file lokal.
Berikut adalah beberapa contoh penggunaan level logging di Laravel:

Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);

Setiap level logging memiliki fungsinya masing-masing dan sebaiknya digunakan sesuai dengan
konteks dan tingkat urgensinya.
Best practice logging
Berikut adalah beberapa Best practice dalam menerapkan logging di aplikasi Laravel:

1. Gunakan log level yang sesuai - pastikan menggunakan level yang tepat sesuai dengan
tingkat kepentingan informasi
2. Struktur log yang konsisten - format log harus konsisten dan mudah dibaca, idealnya
menggunakan format JSON
3. Rotasi log file - terapkan rotasi log untuk menghindari file log yang terlalu besar dan
memakan storage, pada .env laravel set configurasi berikut ini APP_LOG=daily
4. Informasi kontekstual - sertakan informasi yang membantu debug seperti timestamp,
user ID, request ID
5. Hindari data sensitif - jangan mencatat informasi sensitif seperti password atau token
dalam log

Contoh nya sebagai berikut

{
"timestamp": "2025-02-23T12:34:56Z",
"level": "error",
"message": "Database connection failed",
"context": {
"exception": {
"class": "PDOException",
"message": "SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost'",

44
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Database/Connection.php"
"line": 150
},
"request": {
"method": "POST",
"url": "/login",
"ip": "192.168.1.1",
"user_id": 12345
}
}
}

Ĺ Note

Untuk memudahkan melihat isi log yang dihasilkan oleh aplikasi, kita bisa menggunakan
package opcodesio/log-viewer

Selain itu, penting untuk melakukan monitoring terhadap ukuran log file dan mengimplemen-
tasikan strategi pembersihan log yang efektif. Log yang terlalu besar dapat mempengaruhi
performa sistem dan menyulitkan proses analisis.

Rotasi FIle Log

Untuk mengatasi hal ini, sebaiknya menerapkan strategi rotasi log yang teratur dan menyim-
pan log historis di storage terpisah. Implementasi tools monitoring seperti ELK Stack atau
Graylog juga dapat membantu dalam mengelola dan menganalisis log secara lebih efektif.
Selain itu, penggunaan log rotation dan monitoring tools juga membantu dalam mengop-
timalkan penggunaan storage dan mempermudah proses analisis log. Dengan menerapkan
praktik logging yang baik, developer dapat lebih mudah melakukan troubleshooting dan main-
tenance aplikasi.
Untuk memastikan efektivitas logging, penting juga untuk melakukan review berkala terhadap
log yang dihasilkan dan memastikan bahwa informasi yang dicatat benar-benar berguna untuk
proses debugging dan analisis. Implementasi logging yang baik harus seimbang antara detail
informasi yang dicatat dan overhead yang dihasilkan pada sistem.

Monitoring Aplikasi Laravel

Untuk melakukan monitoring aplikasi laravel yang telah kita buat, tersedia banyak tools yang
bisa digunakan, berikut adalah beberapa tools yang sering digunakan untuk melakukan mon-

45
itoring pada aplikasi Laravel. Tools-tools ini memiliki keunggulan masing-masing dan dapat
disesuaikan dengan kebutuhan project yang sedang dikerjakan.
Laravel telescope
Laravel Telescope adalah package debugging yang powerful untuk aplikasi Laravel. Package
ini menyediakan insight mendalam tentang request yang masuk, exception, query database,
queued jobs, mail, notifikasi, cache operations, scheduled tasks, variable dumps dan banyak
lagi. Telescope membantu developer memahami apa yang terjadi di dalam aplikasi secara real-
time dan mempermudah proses debugging. Informasi lebih lengkap dapat klik link berikut
ini.
Health Checkpoint
Health checkpoint adalah fitur yang memungkinkan kita untuk memonitor kesehatan aplikasi
dengan melakukan pengecekan terhadap komponen-komponen kritis seperti koneksi database,
cache, queue, dan layanan eksternal. Dengan mengimplementasikan health checks, kita dapat
mendeteksi masalah sebelum berdampak pada pengguna.
Kita bisa membuat sendiri atau menggunakan package seperti spatie/laravel-health yang
dapat digunakan untuk mengimplementasikan health checks dengan mudah.
Application Performance Monitoring
Beberapa tools APM (Application Performance Monitoring) populer yang dapat digunakan
untuk memonitor aplikasi Laravel, contohnya Sentry, New Relic, Datadog, dan Scout APM.

Ĺ Note

Gunakan Sentry unutk tools APM yang ramah pemula, dan terdapat free tier untuk
belajar.

Tools ini menyediakan insight mendalam tentang performa aplikasi, memungkinkan developer
untuk mengidentifikasi bottleneck, melacak response time, dan menganalisis penggunaan re-
sources.
Dengan menggunakan APM tools, tim dapat proaktif mendeteksi dan menyelesaikan masalah
performa sebelum mempengaruhi pengalaman pengguna.

Kesalahan Umum Logging dan Monitoring

Terlalu banyak log


Logging terlalu banyak informasi yang tidak diperlukan dapat menyebabkan overhead pada
sistem dan mempersulit proses analisis. Sebaiknya fokus pada informasi yang benar-benar

46
penting dan relevan untuk debugging dan monitoring. Pastikan untuk menyaring data yang
dicatat agar log tetap ringkas dan bermakna.
Tidak peduli log level
Menggunakan log level yang tidak tepat atau mengabaikan penggunaan level yang berbeda
dapat menyebabkan kesulitan dalam mengidentifikasi dan memprioritaskan masalah. Penting
untuk memahami kapan menggunakan level debug, info, warning, atau error sesuai dengan
konteksnya. Penggunaan log level yang tepat membantu dalam proses troubleshooting dan
memudahkan dalam memfilter log berdasarkan tingkat kepentingannya.
Tidak manage file log
Jika memiliki storage yang terbatas, file log bisa menjadi salah satu yang memakan banyak
storage, maka dari itu perlu dilakukan hapus seara berkala dalam kurun waktu tertentu. Hal
ini bisa menghemat storage dan memudahkan tools melakukan analisis file log.

47
N+1 Query Problem

Secara sederhana n+1 adalah problem yang seringkali terjadi ketika aplikasi melakukan query
ke database, problemnya terletak pada jumlah query yang berlebihan, sehingga menurunkan
performa dari aplikasi yang sedang berjalan.
N+1 Sering terjadi ketika aplikasi yang kita buat menggunakan ORM, pada konteks Laravel
N+1 sering terjadi ketika kita menuliskan query dengan menggunakan Elaqunent ORM

Studi kasus N+1 Query Problem

Contoh Masalah

Pada aplikasi yang kita buat memiliki tabel Campaigns dan Comments, setiap campaign bisa
memiliki banyak comments, pada suatu halaman kita akan menampikan data campaigns dan
comments yang terkait. Maka seringkali melakukan query dengan menggunakan syntax seperti
dibawah ini

// CampaignController -> index


$campaigns = Campaigns::where('status','=','active')->get();

foreach ($campaigns as $campaign) {


echo $campaign->title . ":\n";
// Setiap kali mengakses $campaign->comments, query baru dijalankan:
// SELECT * FROM comments WHERE campaign_id = ?
foreach ($campaign->comments as $comment) { // N query untuk mengambil comments
echo "- " . $comment->content . "\n";
}
}

Pada query pertama adalah SELECT * FROM campaigns WHERE status = ‘active’ lalu un-
tuk setiap campaigns aplikasi melakukan query kembali untuk menampilkan data comments
yang terkait dengan campaign, query yang dilakukan adalah SELECT * FROM comments WHERE
campaign_id = ?
Misalknya terdapat data 100 Campaign maka query yang dilakukan adalah 101 Query, dengan
penjelasan

48
• Query utama: 1 untuk menggambil data Campaign dan menyimpannya pada variable
$campaigns
• Query tambahan: 100 masing - masing untuk mengambil data Commments yang terkait

Untuk memudahkan pemahaman mengenai N+1 Query kamu bisa melihat diaragm dibawah
ini:

49
Figure 1: N+1 Query Problem

50
Solusi utama: Eager Loading

Solusi N+1 Query pada Laravel adalah Eager Loading, dengan menggunakan eager loading
maka seluruh data akan diambil dalam satu query utama. Eager loading memungkinkan kita
untuk mengambil data pada tabel yang saling berhubungan, misalnya tabel Campaigns dan
Comments.
Untuk menerapkan eager loading maka hal yang perlu dilakukan adalah mendefinisikan rela-
tion pada masing - masing model tabel yang saling berhubungan, misalnya:

// campaigns model
class Campaign extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}

// comments model
class Comment extends Model
{
public function campaign()
{
return $this->belongsTo(Campaign::class);
}
}

Kata kunci utama untuk mengaktifkan eager loading pada tiap query yang dibuat adalah
with() , berikut ini adalah contoh implementasi.

// CampaignController -> index


$campaigns = Campaigns::where('status','=','active')->with('comments')->get();

foreach ($campaigns as $campaign) {


echo $campaign->title . ":\n";
// Data comments sudah ada di memori, tidak ada query baru
foreach ($campaign->comments as $comment) {
echo "- " . $comment->content . "\n";
}
}

Dengan kode diatas maka query yang dihasilkan adalah:

51
1. SELECT * FROM campaigns where status = ‘’active”;
Query ini mengambil data utama campign, misal terdapat 100 active campaigns, maka
akan mendapatkan ID: 1,2,3,4,5 … 100
2. SELECT * FROM comments WHERE campaign_id IN (1, 2, 3, ..., 100);
Query ini akan menggambil semua data komentar untuk 100 campaigns sekaligus, hasil-
nya dalam memory sudah terdapat data 100 campaigns dan semua komentar yang terkait
dengan campaigns

Perbandingan Jumlah Query

JIka dibandingkan maka berikut ini adalah jumlah query dari masing - masing metode yang
telah kita implementasikan

Metode Query Utama Query Tambahan Total Query


Tanpa Eager Loading 1 100 101
Dengan Eager Loading 1 1 2

Maka dapat kita simpulkan bahwa:

• Tanpa Eager Loading, aplikasi melakukan11 queryuntuk mengambil 10CampaigndanCommentterkait.


• Dengan Eager Loading, aplikasi hanya melakukan2 queryuntuk mengambil data
yang sama.
• Eager Loading secara signifikan mengurangi jumlah query, meningkatkan performa ap-
likasi, dan mengurangi beban pada database.

Bagaimana Data Comments Dipetakan ke Campaign?

Setelah ke 2 query dijalankan, Laravel Elaquent akan secara otomatis memetakan ke data
comments ke Campaigns yang sesuai menggunakan campaign_id pada tabel comments. Proses
ini disebut dengan data Hydration

Fitur Lanjutan Eager Loading di Laravel

Eager Loading Multiple Relationships

Kita bisa melakukan eager loading pada beberapa relasi sekaligus, ini sangat berguna untuk
tabel yang memiliki relasi lebih dari 1 ke tabel lainnya, misalkan tabel campaigns dengan
tabel comments dan tabel user contoh penulisannya adalah sebagai berikut:

52
$campaigns = Campaign::with(['comments', 'user'])->get();

Nested Eager Loading

Seringkali kita ingin mengambil data dari relasi yang berelasi, misal kita ingin menggambil
data author dari tabel comments, maka kita bisa menulis sintax seperti dibawah ini:

$campaigns = Campaign::with('comments.user')->get();

Constraining Eager Loads

Seringkali kita ingin melakukan eager loading pada suatu relasi dan menambahkan kondisi
pada query yang kita buat, hal ini bisa kita lakukan dengan memberikan array ke method
with() .
Misalnya kita hanya ingin menampilkan Campaign yang memiliki komentar lebih dari 10,
maka kita bisa menggunakan query seperti dibawah ini

$campaigns = Campaigns::where('status','=','active')
->with(['comments' => function (Builder $query) {
$query->count() > 10;
}])->get();

Kesimpulan

Eager Loading sangat berguna untuk melakukan akses data yang berelasi karena memu-
ngkinkan pengambilan data secara efisien dalam jumlah query yang minimal. Dengan teknik
ini, Anda tidak hanya menghemat sumber daya database tetapi juga meningkatkan respon-
sivitas aplikasi secara signifikan Eager Loading wajib dipakai untuk membuat aplikasi lebih
scalable

53
Mempercepat Load Data dengan Caching
Layer

Caching merupakan salah satu metode yang sering kali di lakukan untuk meningkatkan per-
forma aplikasi, cache digunakan sebagai secondary data store untuk menyimpan data yang
sering diakses oleh pengguna, cache menggunakan memori komputer sebagai media untuk
menyimpan data sehingga memiliki kecepatan membaca data yang sangat cepat.
Data yang disimpan dalam cache merupakan data hasil proses yang dinilai memakan biaya
tinggi dalam prosesnya (misal: koneksi ke basis data), atau bisa juga data yang sering di akses
dan jarang ada perubahan.
Laravel memiliki banyak cache driver yang kita bisa gunakan, saat ini kita bisa memiliih
File, Database, Redis, Memcached, Array dan Dynamodb, yang perlu kita lakukan hanyalan
mengubah environtment variable CACHE_DRIVER . Pada bab kali ini kita akan menggunakan
driver Redis sebagai cache Layer.

Tanpa Menggunakan Cache Layer

Figure 1: Tanpa Cache Layer

User akan melakukan request ke salah satu endpoint yang disediakan, kemudian diproses oleh
service yang di tuju, setelah itu service akan menjalankan fungsional yang telah di tentukan,
misal melakukan sanitasi input, selanjutnya melakukan proses query ke basis data, semua

54
proses yang dilakukan memakan waktu kurang lebih selama 250ms, setelah itu akan
mengembalikan hasil ke user.
Jika dilakukan kalkulasi total waktu yang dibutuhkan dari request sampai dengan user respond
adalah 300ms. dengan estimasi 250ms untuk proses query database dan 50ms untuk proses
komputasi.
Jika service di akses hanya puluhan user mungkin tidak akan ada masalah, namun masalah
akan timbul jika ada ratusan user melakukan akses secara bersamaan. Salah satu sekenario
terburuk adalah database mengalami crash karena tidak bisa menangani banyaknya request.

Menggunakan Cache Layer

Figure 2: Menggunakan Cache Layer

Jika suatu aplikasi memiliki banyak pengguna dan terdapat banyak proses yang berat didalam-
nya maka sudah saat-nya untuk melakukan implementasi caching.
Request pertama yang dilakukan oleh user akan sama seperti tanpa menggunakan cache,
karena kondisi cache yang kosong. Setelahnya hasil akan disimpan dalam Cache untuk di
akses oleh request selanjutnya. Request ke 2 dan seterusnya tidak akan melakukan query ke
database, hal pertama yang dilakukan adalah mencari apakah data yang diminta ada di dalam
Cache, jika ada data akan di ambil dan di kembalikan ke user, jika tidak proses akan berlanjut
dengan melakukan query ke database.

55
Redis

Redis adalah tempat penyimpanan data (database) yang sangat cepat, karena redis menyim-
pan data yang diterima-nya pada RAM komputer bukan di hardisk seperti database biasa.
untuk belajar lebih lanjut kalian bisa mengunjungi Website Redis

Kenapa Redis cocok banget buat cache?

• Cepat banget – Karena Redis nyimpen data langsung di RAM, kecepatan read/write-
nya jauh di atas database biasa atau file.
• Support struktur data kompleks – Redis bukan cuma nyimpen string, dia bisa
simpen list, set, hash, sorted set, dan lain-lain. Jadi kamu bisa simpen data kayak kamu
coding aja.
• Atomic operation – Operasi di Redis bisa dijalankan secara atomik, jadi aman buat
proses paralel.
• Support pub/sub – Redis bisa dipakai buat real-time communication (kayak notifikasi
atau chat) lewat publish/subscribe.

Optimasi Query di Aplikasi Donasi Online

Di aplikasi donasi online yang kita gunakan sebagai studi kasus terdapat halaman
menampilkan detail dari sebuah program donasi tertentu, pada halaman tersebut
ada banyak informasi seperti:

• Judul donasi
• Deskripsi lengkap
• Jumlah donasi yang sudah terkumpul
• Riwayat donatur
• Target donasi
• Progress bar, dll.

Nah, data ini biasanya diambil dari beberapa tabel di database, dan juga perlu ngitung to-
tal donasi atau jumlah donatur dari tabel yang datanya sudah besar banget. Karena harus
ngambil dan ngolah banyak data, maka proses ini bisa jadi lambat, terutama kalau banyak
pengguna buka detail donasi yang sama secara bersamaan.

56
Implementasi Cache Donasi Online

Setelah kita tau problemnya sekarang kita akan melakukan caching pada data yang akan
ditampilkan kepada user di halaman detail donasi, berikut ini adalah ilustrasi alur proses
bisnis dari kode yang akan kita buat.

Figure 3: Implementasi cache Layer

Dapat kita lihat kertika user melakukan request ke server untuk ditampilkan data detail
donasi, maka server akan melakukan pemeriksaan terlebih dahulu ke Redis/Cache, jika tidak
ditemukan data yang dimaksud maka server akan melakukan query ke database, setelah men-
dapatkan data yang dimaksud data tersebut akan di simpan dalam cache dan kemudian akan
diberikan kembali ke user.
Jika diagram diatas di konversikan menjadi kode pada controller, maka akan menghasilkan
seperti dibawah ini.

use Illuminate\Support\Facades\Cache;
use App\Models\Donation;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

public function show($slug)


{
$cacheKey = "donation_detail_{$slug}";
$cacheDuration = now()->addMinutes(10);

$donationData = Cache::remember($cacheKey, $cacheDuration, function () use ($slug) {

57
// Ambil data utama donasi + kategori
$donation = Donation::with('category')
->where('slug', $slug)
->firstOrFail();

// Total donasi yang sudah terkumpul


$totalCollected = $donation->donors()->sum('amount');

// Total donatur unik


$uniqueDonors = $donation->donors()->distinct('user_id')->count('user_id');

// Rata-rata donasi
$averageDonation = $donation->donors()->avg('amount');

// Donasi terbaru (misalnya 5 terakhir)


$recentDonors = $donation->donors()
->with('user') // jika kamu ingin ambil nama user-nya
->orderByDesc('donated_at')
->take(5)
->get();

// Donasi per periode waktu


$now = Carbon::now();
$donationToday = $donation->donors()
->whereDate('donated_at', $now->toDateString())
->sum('amount');

// Progress donasi dalam persen


$progress = $donation->target_amount > 0
? round(($totalCollected / $donation->target_amount) * 100)
: 0;

// Gabungkan semua ke array


return [
'donation' => $donation,
'total_collected' => $totalCollected,
'unique_donors' => $uniqueDonors,
'average_donation' => $averageDonation,
'recent_donors' => $recentDonors,
'donation_today' => $donationToday,
'status' => $status,
'progress' => $progress,

58
];
});

return view('donations.show', $donationData);


}

Dapat kita lihat pada kode diatas kita melakukan import classs Cache use Illuminate\Support\Facades\Cache
. selain itu kita juga menggunakan beberapa fungsi lain seperti

Syntax Description
$cacheKey = Untuk membuat cache key berdasarkan slug
"donation_detail_{$slug}"; donasi. Ini penting supaya data cache tiap
donasi tidak bentrok.
Cache::remember($cacheKey, Cek apakah data sudah ada di cache. Kalau
now()->addMinutes(10), function () belum, jalankan query di dalam closure, lalu
use ($slug) { ... }); simpan hasilnya ke cache Redis selama 10
menit.
now()->addMinutes(10) Menentukan waktu kedaluwarsa cache
(dalam contoh ini: 10 menit). Setelah itu,
cache otomatis dianggap kadaluarsa dan
akan dibuat ulang.

Hapus cache jika data sudah tidak diperlukan

Bagaimana jika sebelum waktu-nya kedaluarsa sumber data yang asli sudah berubah, maka
user akan tetap melihat data lama dari cache, bukan data terbaru dari database. Ini adalah
risiko utama saat menggunakan cache, terutama pada data yang sering berubah.
Maka dari itu hal yang bisa dilakukan adalah menghapus Cache ketika data berubah, hal
ini bisa dilakukan dengan melakukan implementasi pada fungsi yang mengubah data Donasi,
misalnya:

public function updated(Donation $donation)


{
/ logic ketika update ..
Cache::forget("donation_detail_{$donation->slug}");
}

Dengan begitu, ketika ada request untuk menampilkan kembali data, sistem akan mengambil
data terbaru dari database dan menyimpannya kembali ke dalam cache yang baru. Pendekatan

59
ini membantu menjaga konsistensi data dan mencegah tampilan data lama akibat cache yang
tidak diperbarui.

60
Menggunakan Migrations dan Seeder secara
Maksimal

Latar Belakang

Saya seringkali menemukan project Laravel yang tidak memanfaatkan fitur migrations dengan
maksimal, akibatnya muncul banyak masalah yang seharusnya tidak perlu ada kalau dari awal
sudah menggunakan fitur migrations, masalah yang muncul diantaranya
Proses development, 1). ketika dalam satu tim terdapat banyak developer maka ketika ada
satu saja developer yang menggubah struktur database maka developer lain yang terlibat perlu
mengubah secara di komputer mereka, baik secara manual maupun dengan export-import
schema database. 2). perubahan struktur database menjadi tidak terdokumentasi, sehingga
anggota tim tidak mengetahui alasan kenapa suatu tabel/kolom mengalami perubahan
Proses deployment. Pada tahap ini jika terjadi perubahan pada skema database maka de-
veloper harus melakukan import-export data yang ada di komputer mereka ke server produc-
tion, perilaku seperti ini sangat membahayakan dan rawan membuat aplikasi crash sehingga
menimbulkan downtime.
Maka dari itu penggunakan migration dan seeder sangatlah di rekomendasikan untuk memu-
dahkan proses development sampai dengan proses deployment

Larevel Migrations dan Seeder

Migrations dan seeders adalah 2 fitur penting pada Laravel yang tidak bisa di pisahkan.
Migrations seperti blueprint untuk database tugasnya adalah menentukan struktur tabel dan
kolom dalam aplikasi, dengan migration kita bisa menambah/mengurangi/mengubah stuktur
tabel dan kolom dengan menggunakan syntax php. Selain itu ketika kita tergabung dengan
suatu tim kita bisa sync stuktur database antar anggota dengan mudah cukup menjalankan
Seeder fungsinya untuk mengisi data kedalam database, biasanya untuk input data dummy
atau data awal aplikasi, misal kita butuh data 100 user admin di aplikasi dengan seeders kita
bisa input data tersebut dengan mudah di aplikasi
Perbedaan Migration dan seeders

61
Migration fokus ke bentuk dan struktur database—kayak bikin tabel, nambah kolom, atau
hapus kolom. Seeder fokus ke isinya, alias ngisi tabel itu dengan data. Jadi gampangnya,
migration = bentuknya, seeder = isinya. Dua-duanya saling melengkapi, kayak tim yang
solid!

Menggunakan Migration

Untuk membuat migrations kita bisa dengan mudah menggunakan php artisan dengan com-
mand make:migration . setiap command di eksekusi maka akan terbuat file baru pada folder
database/migrations dengan nama file yang berisikan timestamp saat file dibuat.
Contoh pembuatan migration seperti dibawah ini

php artisan make:migration create_campaign_table

maka akan terbuat file baru, selanjutnya kita bisa mendefinisikan kolom dan tipe data yang
ada dalam tabel donations, kamu bisa melihat daftar data type yang di dukung Laravel melalui
link berikut https://laravel.com/docs/12.x/migrations#available-column-types
Jika sudah maka idealnya suatu file migrations akan seperti dibawah ini

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration


{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('campaigns', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->string('thumbnail')->nullable();
$table->text('description');
$table->integer('status')->default(0);
$table->dateTime('start_date');

62
$table->dateTime('end_date');
$table->decimal('amount', 15, 2);
$table->foreignId('user_id')->constrained();
$table->softDeletes();
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('campaigns');
}
};

Untuk menjalankan migrations kita cukup menggunakan command php artisan migrate
Berikut ini adalah command yang biasa digunakan dalam menggunakan fitur migrations

Perintah Kegunaan
php artisan make:migration Membuat file migration baru. Biasanya
nama_migration digunakan untuk membuat, mengubah, atau
menghapus tabel.
php artisan migrate Menjalankan semua migration yang belum
dijalankan ke database.
php artisan migrate:rollback Mengembalikan (rollback) batch terakhir
dari migration yang sudah dijalankan.
php artisan migrate:reset Mengembalikan semua migration ke awal
(semua migration dibatalkan).
php artisan migrate:refresh Kombinasi dari reset lalu migrate, sering
digunakan untuk reset dan mengisi ulang
database.
php artisan migrate:fresh Menghapus semua tabel dan menjalankan
ulang semua migration dari awal. Cocok
untuk development.
php artisan migrate:status Menampilkan status dari setiap migration,
apakah sudah dijalankan atau belum.
php artisan migrate:install Membuat tabel migrations di database,
yang digunakan Laravel untuk menyimpan
status migration.

63
Perintah Kegunaan
php artisan migrate Menjalankan migration dari file tertentu
--path=path/to/migration.php saja.
php artisan migrate --step Menjalankan migration dengan menyimpan
step-nya, sehingga bisa di-rollback per
langkah.

Menggunakan Seeder

Sebelum menggunakan Seeder pastikan pada Model telah di definisikan $fillable karena
seeder akan menggunakan mass asigments untuk insert data.

protected $fillable = [
'name', 'slug', 'thumbnail', 'description', 'status',
'start_date', 'end_date', 'amount', 'user_id'
];

Membuat file seeder cukup menjalankan command php artisan make:seeder dengan begitu
akan terbuat file baru di folder database/seeders, contohnya kita akan membuat seeder untuk
data Campaign maka kita bisa menjalankan command dibawah ini:

php artisan make:seeder CampaignSeeder

Maka akan terbuat file baru seperti dibawah ini

<?php

namespace Database\Seeders;

use App\Models\Campaign;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;

class CampaignSeeder extends Seeder


{
/**
* Run the database seeds.
*/

64
public function run(): void
{
/// isi dari seeder
}
}

Inti dari file ini ada pada method run() kita bisa menggunakan faker untuk mengisi data
dummy, Pada contoh dibawah kita akan memebuat 5 data dummy untuk Campaign dengan
memanfaatkan faker.

<?php

namespace Database\Seeders;

use App\Models\Campaign;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;

class CampaignSeeder extends Seeder


{
/**
* Run the database seeds.
*/
public function run(): void
{
$user = User::first() ?? User::factory()->create();

for ($i = 0; $i < 5; $i++) {


$name = fake()->sentence(3);
Campaign::create([
'name' => $name,
'slug' => Str::slug($name) . '-' . $i,
'thumbnail' => fake()->imageUrl(640, 480, 'charity', true),
'description' => fake()->paragraph(),
'status' => fake()->numberBetween(0, 2), // 0: draft, 1: active, 2: archived
'start_date' => now()->subDays(rand(1, 30)),
'end_date' => now()->addDays(rand(30, 90)),
'amount' => fake()->randomFloat(2, 1000000, 10000000),
'user_id' => $user->id,
]);
}

65
}
}

Seringkali kita memiliki banyak file seeder, dari pada menjalakan satu persatu file kita bisa
menggabungkan semua file seeder ke dalam satu file yaitu pada DatabaseSeeder.php di ma-
sukan dalam method call

class DatabaseSeeder extends Seeder


{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call([
CampaignSeeder::class,
DonationSeeder::class,
]);
}
}

Ada beberapa command yang bisa di eksekusi untuk menjalankan seeder, berikut ini adalah
command yang biasa digunakan:

Perintah Kegunaan Contoh Penggunaan


php artisan db:seed Menjalankan seeder default php artisan db:seed
(DatabaseSeeder)
php artisan db:seed Menjalankan seeder tertentu php artisan db:seed
--class=NamaSeeder --class=CampaignSeeder
php artisan migrate Jalankan migrasi lalu seeder php artisan migrate
--seed default --seed
php artisan db:seed Jalankan seeder tanpa php artisan db:seed
--force konfirmasi (biasanya untuk --force
production)

Membuat Migration dan Seeder yang baik

1. Gunakan naming convention yang jelas untuk migration

66
Dalam membuat migration, sangat penting untuk menggunakan naming convention
yang jelas dan deskriptif. Misalnya, nama seperti create_donations_table atau
add_status_to_users_table akan langsung menggambarkan maksud dari migration.
Hal ini membantu dalam membaca kode, dan juga memudahkan kolaborasi tim dan
proses debugging jika terjadi masalah.
2. Hindari perubahan langsung pada database production tanpa migration
Semua perubahan struktur database sebaiknya dicatat melalui migration supaya bisa
dilacak, dirollback, dan direplikasi dengan konsisten ke environment lain misal staging
atau local development. Langkah ini penting untuk menjaga stabilitas dan integritas
database, serta menghindari error.
3. Gunakan seeder dengan data realistis untuk testing
Saat menggunakan seeder, pastikan untuk mengisi data dengan contoh yang real-
istis. Misalnya, jika membuat data untuk donasi, isi kolom seperti nama donor, jumlah
donasi, dan tanggal donasi dengan nilai yang masuk akal. Data yang realistis membantu
pengujian aplikasi menjadi lebih valid dan mendekati kondisi produksi. Faker bisa digu-
nakan untuk meng-generate data palsu, tapi tetap perlu dikontrol agar hasilnya tetap
natural.

67
Menyusun Routing untuk Project Besar

Laravel merupakan framework yang menerapkan desain arsitektur MVC, yang artinya
memisahkan antara Model (data), View (tampilan), dan Controller (logika aplikasi).
Meskipun routing tidak termasuk dalam definisi formal MVC, perannya routing dalam
Laravel sangat krusial. Routing bertindak sebagai penghubung utama yang memas-
tikan permintaan dari pengguna sampai ke bagian logika yang tepat di dalam
Controller.
Secara praktis, routing bertanggung jawab memetakan alamat URL yang diminta pengguna
dan metode HTTP yang menyertainya (GET, POST, dll.) ke suatu fungsi atau method
spesifik di Controller. Ketika sebuah permintaan diterima oleh aplikasi Laravel, sistem routing
inilah yang pertama kali bekerja untuk menentukan kode mana yang harus dieksekusi sebagai
respons.
Routing bisa dikatakan sebagai jalan yang mengarahkan request pengguna ke controller dan
method yang sesuai, yang selanjutnya method tersebut akan memberikan response sesuai
dengan logika yang ada didalamnya.

Berbagai File Router dan Kegunaan-nya

Ketika melakukan instalasi Laravel maka kita akan mendapatkan folder router yang didalam-
nya terpadat file-file berikut ini:

• web.php route ini digunakan untuk route aplikasi web pada umumnya, terdapat fitur
stateful dan perlindungan CSRF
• api.php digunakan untuk membuat API yang bersifat stateless, route ini cocok untuk
membuat restfull api yang pembuatan SPA
• channels.php tidak diakses oleh pengguna secara langsung namun file ini digunkan
untuk keperluan modern web application, seperti websocket untuk keperluan realtime
pada suatu webiste
• console.php tempat dimana kita bisa mendefinisikan semua perintah untuk artissan
console, misalnya membuat scheduler

68
Maksimalkan router Laravel

Ketika bekerja dengan project yang besar, fitur router pada Laravel seringkali menjadi
kompleks dan sulit untuk di mantain jika kita tidak mengaturnya dengan baik,
semakin banyak fitur yang kita buat, semakin banyak juga route yang perlu di definisikan.
Seringkali file route menjadi sangat panjang sehingga susah untuk dibaca dan tentunya juga
sangat menyulitkan kalau kita mau menambahkan rute baru.
Maka dari itu saya menerapkan metode di bawah ini dalam proses development proyek besar,
untuk membantu memastikan struktur rute tetap rapi, mudah dipahami, dan efisien dalam
pengelolaan:

Menggunakan named router

Named router adalah aktifitas memberikan nama pada suatu route yang kita definisikan, hal
ini bisa kita lakukan dengan menggunakan method →name(’nama.route’) contoh-nya sebagai
berikut:

Route::get('/donation', [DonationController::class, 'index'])->name('donation.index');

Sehingga ketika kita akan melakukan refferensi ke suatu URL bisa menuliskan route(’nama.route’)
pada komponent manapun di aplikasi yang kita buat.
Penggunaan named route memiliki banyak keuntungan, diantaranya adalah:

1. Mudah untuk melakukan maintance kode


Dalam proses development seringkali kita melakukan perubahan pada URL, jika sudah
menggunakan named route maka kita hanya perlu melakukan modifikasi pada rute di
routes/web.php . Semua refferensi ke route yang kita ubah akan diperbarui secara
otomatis
2. Kode mudah dibaca
Menggunakan nama route memudahkan kita untuk mengetahui fungsi dan tujuan dari
suatu URL dibuat, kita tidak perlu mengingat URL panjang pada aplikasi
3. Penulisan URL menjadi lebih mudah
Ketika kita membutuhkan membuat link atau melakukan redirect ke suatu URL maka
kita hanya perlu memanggil named route dengan menuliskan route("name.route')
contohnya

69
// link ke profile

<a href="{{ route('users.show', ['id' => $user->id]) }}">Lihat Profil</a>

// membuat form action


<form action="{{ route('posts.store') }}" method="POST">
@csrf
</form>

// redirect setelah create action


public function create(Request $request)
{
#logic create
return redirect()->route('posts.index')
}

Menggunakan resource router untuk CRUD

Untuk operasi Create, Read, Update, dan Delete (CRUD) pada suatu model, saya gunakan
route resource untuk memperpendek penulisan di file web.php. Misalnya saya membuat con-
troller khusus untuk CRUD donation maka saya hanya perlu menuliskan

// web.php
Route::resource('donation', DonationController::class)

Maka secara otomatis akan medefinisikan route dibawah ini dan terhubung dengan metode-
metode yang sesuai pada controller

HTTP Method URL Controller Method Route Name


GET /donation index donation.index
GET /donation/create create donation.create
POST /donation store donation.store
GET /donation/{donation} show donation.show
GET /donation/{donation}/edit edit donation.edit
PUT/PATCH /donation/{donation} update donation.update
DELETE /donation/{donation} destroy donation.destroy

Jika tanpa menggunakan route resource maka kita perlu menulis semua route dibawah ini,
dapat kita bandingkan berapa line yang bisa dihemat jika menggunakan Route::resource

70
// ditulis secara manual
Route::get('/donation', [DonationController::class, 'index'])->name('donation.index');
Route::get('/donation/create', [DonationController::class, 'create'])->name('donation.create'
Route::post('/donation', [DonationController::class, 'store'])->name('donation.store');
Route::get('/donation/{donation}', [DonationController::class, 'show'])->name('donation.show'
Route::get('/donation/{donation}/edit', [DonationController::class, 'edit'])->name('donation.
Route::put('/donation/{donation}', [DonationController::class, 'update'])->name('donation.upd
Route::delete('/donation/{donation}', [DonationController::class, 'destroy'])->name('donation

Menggunakan route model binding

Dengan menggunakan route model binding kita bisa mengambil data dari model berdasarkan
parameter yang ada di URL, lalu menggunakan objek tersebut ke dalam controller untuk di
lakukan pemroresan lebih lanjut.
Bayangkan kamu punya route seperti ini:

Route::get('/donation/{donation}', [DonationController::class, 'show'])->name('donation.creat

Tanpa menggunakan route model binding mungkin contoller kalian seperti dibawah ini

public function show($id)


{
$donation = Donation::findOrFail($id);

return view('donation.show', compact('donation'));


}

Namun jika kita menggunakan model binding maka Laravel akan mencoba untuk melakujkan
pencocokan parameter yang ada di router dengan primary key yang ada dalam
database, jika sesuai maka variable yang di assign akan berisikan data yang berasal dari
model sehingga code dalam contoller kita lebih pendek, contohnya sebagai berikut

public function show(Donation $donation)


{
return view('donation.show', compact('donation'));
}

Keuntungan menggunakan Route Model Binding:

• Kode Lebih Bersih dan Ringkas

71
• Meningkatkan security
• Proses development menjadi lebih cepat
• Kode menjadi lebih mudah di baca dan di maintance

Memisahkan file router sesuai context

Ketika dihadapakan dengan project yang besar seringkali file route web.php menjadi sangat
panjang, hal ini kurang baik karena kita akan menjadi sasah untuk maintance file tersebut,
apalagi kalau dalam tim kita ada banyak developer yang secara bersamaan mengedit file yang
sama, maka akan terjadi mimpi buruk git conflict
Maka dari itu, memisahkan file router berdasarkan domain fitur, context atau role sangat
saya rekomendasikan sebagai best practice dalam pengembangan aplikasi laravel pada skala
aplikasi besar-menengah. Hal ini karena kita akan lebih mudah untuk melakukan maintance,
file router kita lebih terorganisir dan memudahkan kita untuk melakukan kolaborasi antar
anggota team.
Cara Memisahkan FIle router
Pada kasus kali ini saya akan memisahkan file router berdasarkan roles yang ada dalam aplikasi,
anggap saya di aplikasi kita terdapat role admin dan user, maka saya akan membuat route
admin.php dan user.php dengan tahapan sebagai berikut:

1. Buat file route baru di folder routes


touch routes/admin.php

2. Mendefinisikan route pada file yang baru kita buat


<?

use Illuminate\Support\Facades\Route;

Route::group([
'namespace' => 'App\Http\Controllers\Admin',
'prefix' => 'admin',
'middleware' => ['role:admin']
], function () {
Route::get('','AdminController@dashboard');
Route::get('/users','UserController@index');
....
});

72
3. Mendaftarkan pada sistem Laravel
Jika menggunakan Laravel 11 Kebawah maka kita perlu mendaftarkan file yang baru
saja kita buat ke RouteServiceProvider
public function boot(): void
{
parent::boot();

$this->routes(function () {

// Tambahan route custom


Route::middleware(['web'])
->group(base_path('routes/admin.php'));
});
}

Untuk pengguna Laravel 12+ maka perlu menambahkan pada bootstrap/app.php pada
function withRouting contohnya sebagai berikut
<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Sentry\Laravel\Integration;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function(){
Route::prefix('admin')
->middleware('admin')
->group(base_path('routes/admin.php'));
}
)

->withExceptions(function (Exceptions $exceptions) {


Integration::handles($exceptions);

})->create();

Dengan demikian file router kita sudah terorganisir dengan baik, dan akan memudahkan kita

73
untuk mengerjakan project yang cukup besar.

Jangan gunakan clousure file routing

Laravel memperbolehkan kita untuk menuliskan clousure pada router secara langsung, con-
tohnya seperti berikut

<?php

use Illuminate\Support\Facades\Route;

Route::get('/welcome/{name?}', function ($nama = 'Pengunjung') {


return 'Halo, ' . htmlspecialchars($nama) . '! Selamat datang.';
});

Route /welcome/{name?} mendefinisikan logika untuk mengembalikan string yang menyapa


pengguna di dalam Closure, meskipun kita bisa menuliskan clousure pada file router secara
langsung hal ini sangat tidak disarankan. Berikut ini adalah alasanya:

1. Membuat kode susah untuk di baca


2. Membuat file roter menjadi gemuk
3. Tidak sesuai dengan prinsip SOLID - Single Responsibility Principle
4. Menyulikan kolaborasi antar anggota team

Meskipun terlihat sederhana diawal jika kita menuliskan Closure pada router kedepannya hal
ini akan menjadi masalah yang serius pada pengembangan aplikasi. maka jika sudah terlan-
jur sangat disarankan untuk memindahkan logika dari clouse ke dalam controller.
Adanya controller membuat kode yang ditulis menjadi lebih mudah dibaca, dimaintance dan
memungkinkan kita untuk menggunakan semua fitur yang ada di dalam Laravel

74
Helper Function Solusi untuk Kode yang
Reusable dan Konsisten

Ketika kita sedang mengembangkan suatu proyek aplikasi, seringkali kita menuliskan fungsi
yang sama di berbagai tempat, misal saja ada kebutuhan untuk melakukan formating nilai
mata uang pada halaman invoice, maka kita akan menggunakan fungsi number_format .
Hal yang sering terjadi dalah kita tidak hanya melakukan formating rupiah pada satu halaman
biasanya ada kasus dimana kita melakukannya di halaman konfirmasi pembayara, tagihan,
daftar pembelian dan yang lainnya. Menuliskan "Rp " . number_format("10000", 2, ",",
"."); di berbagai tempat tersebut menjadi tidak efektif karena jika ada perubahan format
kita harus melakukannya di berbagai tempat, maka solusinya adalah dengan membuat Helper
Function.

Pengertian Helper Function

Helper function adalah fungsi PHP yang dapat kita panggil dari mana saja dalam aplikasi
yang kita kembangkan tanpa perlu menginstansiasi kelas atau memanggil method tertentu.
Laravel punya banyak helper function bawaan yang sangat berguna untuk berbagai hal, mulai
dari manipulasi string, array, number dan sampai dengan debugging. Selain itu kita juga bisa
membantu Helper function yang bisa kita sesuaikan dengan kebutuhan, tanpa bergantung
pada helper yang sudah disediakan oleh Laravel.

Kapan Menggunakan Helper Function?

Salah satu pertanyaan yang cukup sering muncul saat mengembangkan aplikasi Laravel adalah,
“Kapan sebaiknya kita menggunakan helper function?” Jawabannya berkaitan erat dengan
tujuan dari helper itu sendiri: menyederhanakan kode yang sering digunakan berulang kali di
berbagai tempat.
Helper function sangat cocok digunakan ketika kita ingin membuat kode yang lebih ringkas
dan mudah digunakan ulang — baik di dalam view, controller, maupun class lain. Biasanya,
fungsi-fungsi yang layak dijadikan helper bersifat sederhana dan stateless, artinya tidak
menyimpan kondisi atau bergantung pada proses lain. Contoh paling umum adalah fungsi
untuk memformat mata uang, mengonversi tanggal, atau memotong teks.

75
Selain itu, helper function idealnya tidak bergantung pada instance lain di aplikasi.
Artinya, fungsi tersebut tidak seharusnya terhubung langsung dengan model, database, atau
memerlukan dependency injection. Begitu sebuah fungsi mulai membutuhkan resource ekster-
nal atau menjadi terlalu kompleks, biasanya itu sudah bukan wilayahnya helper — dan lebih
tepat ditangani oleh service class atau komponen lain yang lebih terstruktur.

Jangan Gunakan Helper function jika

Menambahkan helper function berarti kita menambahkan satu lapisan baru ke dalam struktur
aplikasi. Meskipun helper sangat membantu untuk membuat kode lebih efisien dan reusable,
bukan berarti semua kebutuhan harus diselesaikan dengan helper function. Peng-
gunaan yang tidak tepat justru bisa membuat arsitektur aplikasi menjadi tidak rapi dan lebih
sulit dipelihara.
Karena itu, penting untuk mengenali kapan tidak menggunakan helper function. Hindari
membuat helper function ketika logika di dalamnya sudah terlalu kompleks, melibatkan banyak
kondisi, atau membutuhkan resource seperti dependency atau akses database. Helper bukan
tempat untuk menampung logika berat, lebih baik pindahkan ke service class yang memang
didesain untuk menangani hal tersebut.
Selain itu, jika ternyata kode akan lebih jelas dan mudah dibaca tanpa harus menggunakan
helper function, jangan paksakan penggunaannya. Kadang-kadang, terlalu banyak abstraksi
justru mengaburkan maksud dari kode itu sendiri. Prinsip utamanya tetap: gunakan helper
untuk menyederhanakan, bukan malah membingungkan.

Helper Function Bawaan Laravel

Seperti yang kita semua tau, Laravel telah menyediakan banyak helper function untuk berbagai
keperluan, dari mulai manupulasi string, tipe data hingga debungging, semua helper function
tersebut bisa kita baca pada Halaman Dokumentasi Laravel. Beberapa Helper function yang
sering digunakan antara lain:

1. String Manipulation Helpers


• str_limit(): Membatasi panjang string.
• str_slug(): Membuat slug dari string (misalnya untuk URL).
• str_random(): Membuat string acak, berguna untuk token atau password semen-
tara.
2. Array Helpers
• array_except(): Mengambil semua elemen array kecuali yang disebutkan.
• array_has(): Mengecek apakah suatu key ada di dalam array.

76
• array_pluck(): Mengambil nilai dari kolom tertentu dalam array multidimensi.
3. Collection Helpers
• collect(): Membuat koleksi Laravel dari array biasa, memungkinkan penggunaan
berbagai metode chaining.
• collect()->pluck(): Mengambil elemen dari koleksi berdasarkan kunci tertentu.
4. URL dan Path Helpers
• url(): Membuat URL lengkap untuk aplikasi.
• asset(): Membuat URL untuk file asset, seperti gambar atau CSS.
5. Debugging Helpers
• dd(): Dump and die, menampilkan variabel dan menghentikan eksekusi.
• dump(): Menampilkan informasi tentang variabel tanpa menghentikan eksekusi.

Jika semua helper function yang sudah ada, belum juga sesuai dengan kebutuhan kita maka
kita bisa membantu custom helper function yang bebas kita buat sesuai kebutuhan.

Custom Helper function

Walaupun sudah banyak helper function yang ada diLaravel, sering kali kita membutuhkan
fungsi khusus sesuai dengan kebutuhan aplikasi. Untuk itu kita bisa membuat custom helper
functions. Berikut ini adalah langkah-langkahnya:
Membuat file Helper Function
KIta bisa membuat folder khusus yang menyimpan semua file Helper yang ada dalam aplikasi
yang sedang kita kembangkan, biasanya saya membuatnya di folder app/Helpers misalkan kita
membuat StringHelper maka akan menjadi **app/Helpers/Currency**Helper.php Pada
helper file yang saya buat saya menuliskan fungsi untuk melakukan formating ke rupiah.

<?php

if (! function_exists('format_rupiah')) {
function format_rupiah($number) {
return 'Rp ' . number_format($number, 2, ',', '.');
}
}

Mendaftarkan di Composer
Setelah membuat file helper supaya fungsi yangs udah dibuat bisa dieksekusi di berbagai
tempat di Laravel kita maka kita perlu untuk mendaftarkan-nya pada composer.json , kita
akan menambahkan-nya pada bagian autoload > files seperti contoh dibawah ini

77
"autoload": {
"files": [
"app/Helpers/**Currency**Helper.php"
]
}

Setelah menambahkan kita perlu menjalankan perintah composer dump-autoload supaya


helper yang baru bisa digunakan
Menggunakan Helper Custom Helper
Pemanggilan helper function yang telah kita buat bisa dilakukan dimana saja dalam Aplikasi
Laravel kita, contohnya kita bisa memanggil format_rupiah di controller, view bahkan di
service layer

<?php

public function showInvoice($amount) {


return view('detail.invoice', ['amount' => format_rupiah($amount)]);
}

Perbandingan Helper Bawaan vs Custom Helper Function

Fungsi Helper Bawaan Laravel Custom Helper Function


Tujuan Fungsi umum yang sering Fungsi yang disesuaikan
digunakan dalam berbagai dengan kebutuhan spesifik
aplikasi. aplikasi Anda.
Kemudahan Penggunaan Mudah langsung digunakan Perlu penulisan kode dan
tanpa konfigurasi tambahan. registrasi untuk penggunaan.
Fleksibilitas Terbatas pada fungsi yang Sangat fleksibel untuk
disediakan oleh Laravel. disesuaikan dengan
kebutuhan aplikasi.
Scalability Ideal untuk penggunaan Cocok untuk menangani
umum, tidak spesifik. logika khusus yang lebih
rumit.

Helper function, baik yang bawaan atau custom, merupakan alat yang sangat berguna dalam
pengembangan aplikasi Laravel. Fungsi-fungsi ini memungkinkan Anda untuk membuat kode

78
yang lebih efisien dan dapat digunakan ulang, serta membantu menjaga kebersihan dan keter-
bacaan kode. Menggunakan helper dengan bijak dan menyesuaikannya dengan kebutuhan
aplikasi adalah kunci untuk menjaga aplikasi tetap sederhana dan mudah dipelihara.

Best Practice Penggunaan Helper Function

1. Gunakan Hanya untuk Utility Function


Helper function sebaiknya difokuskan untuk kebutuhan yang sifatnya utility — yaitu
fungsi-fungsi kecil yang tidak bergantung pada konteks aplikasi dan bisa digunakan se-
cara universal. Dengan membatasi helper hanya untuk tugas-tugas ringan dan stateless
seperti ini, kita menjaga agar helper tetap sederhana, mudah diuji, dan tidak merusak
struktur aplikasi.
2. Kelompokkan Helper Function Berdasarkan Domain
Untuk menjaga kemudahan dalam mengatur kode, kita baiknya mengelompokkan helper
function berdasarkan domain atau fungsionalitasnya. Misalnya, StringHelper untuk
manipulasi string, NumberHelper untuk format tipe data number, dan DateHelper untuk
tanggal. Dengan begitu kita akan lebih mudah memahami konteks dari setiap fungsi
yang tersedia.
3. Dokumentasikan Helper
Salah satu kesalahan yang sering terjadi adalah membuat helper function lalu lupa men-
dokumentasikannya. Akibatnya, kita tidak tau bahwa suatu fungsi sudah pernah dibuat
dan akhirnya menulis ulang fungsi yang sama.

79
Implementasi Form Request untuk Validasi
Modular

Pernah nggak, kamu bikin controller yang awalnya pendek tapi lama-lama jadi makin panjang
gara-gara validasi input user yang sangat panjang? Hampir semua dari kita yang menggunakan
Laravel untuk mengembangkan aplikasi sudah tidak asing lagi dengan validasi input user pada
controller, pada kasus sederhana saya menuliskannya dengan gaya langsung pada controller,
seperti berikut ini:

<?php

class DonationController extends Controller {

public function store(Request $request)


{
// Validasi input menggunakan method validate() dari Request
$request->validate([
'name' => 'required|string|max:100',
'email' => 'required|email',
'amount' => 'required|numeric|min:10000',
'message' => 'nullable|string|max:500',
'payment_method' => 'required|in:bank_transfer,qris,ewallet',
]);

// ....
}

Tidak ada yang salah dari fungsi yang saya tulis sebelumnya, kita bisa melakukan validasi
request yang diberikan oleh user pada title dan body dengan berbagai aturan yang kita
terapkan.
Namun ketika kita dihadapkan pada suatu kondisi dimana diharuskan untuk melakukan vali-
dasi pada user input yang memiliki banyak variable dan atau dibutuhkan validasi yang memer-
lukan customisasi, sehingga menggunakan validasi bawaan dari laravel tidak cukup, maka
sebagai solusi-nya kita bisa menggunakan FormRequest

80
Laravel Form Request

Form Request merupakan fitur bawaan Laravel yang membuat kita memisahkan validasi
ke dalam kelas tersendiri. Dengan pendekatan ini, kita bisa menulis aturan validasi, pesan
kustom, bahkan transformasi data sebelum sampai ke controller dan semua di satu tempat
yang terpisah dan terstruktur. Kalau controller itu chef, maka FormRequest adalah asisten
dapur yang siap memfilter bahan sebelum dimasak.
Untuk membuat Form Request kita cukup menjalankan perintah php artisan make:request
namaRequest contohnya berikut ini:

php artisan make:request StoreDonationRequest

Laravel akan membuat file baru di dalam folder app/Http/Request didalam file baru tersebut
akan terdapat dua method penting yaitu rules() dan authorize() yang masing-masing
memiliki fungsi sebagai berikut:

• rules() berisi aturan validasi yang ingin kita terapkan


• authorize() menentukan apakah user yang melakukan request diizinkan untuk
melakukannya

Untuk mempermudah pembahasan kali ini, berikut ini adalah contoh dari isi file Form Request
StoreDonationRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreDonationRequest extends FormRequest


{
/**
* Tentukan apakah user diizinkan untuk melakukan request ini.
*/
public function authorize(): bool
{
// Jika tidak menggunakan policy, cukup return true
return true;
}

/**
* Aturan validasi untuk request donasi.

81
*/
public function rules(): array
{
return [
'name' => 'required|string|max:100',
'email' => 'required|email',
'amount' => 'required|numeric|min:10000',
'message' => 'nullable|string|max:500',
'payment_method' => 'required|in:bank_transfer,qris,ewallet',
];
}

Dengan struktur seperti di atas, kita sudah berhasil memindahkan seluruh logika vali-
dasi ke tempat yang lebih terpisah dan modular. Controller sekarang tidak lagi perlu
menuliskan aturan satu per satu, karena Laravel akan otomatis menjalankan validasi dari
Form Request sebelum masuk ke controller. Ini berarti, jika ada input yang tidak valid, maka
user akan langsung mendapatkan respon error — tanpa kita harus menangani manual di
controller.
Untuk menggunakannya juga cukup serderhana, kita hanya perlu mengganti parameter
equest $request di controller menjadi StoreDonationRequest $request Laravel akan
otomatis meng-inject kelas tersebut dan melakukan validasi berdasarkan rules yang sudah
kita tulis sebelumnya.

public function store(StoreDonationRequest $request)


{
$validated = $request->validated();

// Lanjutkan ke proses penyimpanan data atau delegasi ke service


}

Dengan pola ini, controller menjadi jauh lebih bersih dan fokus pada tugas utamanya, Kita
tidak perlu lagi memikirkan aturan validasi di sini, karena semuanya sudah ditangani oleh
Form Request secara terpisah.

Kustomisasi Form Request

Form Request bukan cuma tempat untuk menaruh aturan validasi kita juga bisa melakukan
kustomisasi sesuai dengan kebutuhan. Semakin kompleks aplikasi yang kita bangun, se-
makin besar manfaat dari fitur-fitur kustomisasi ini, baik untuk meningkatkan user experience

82
maupun untuk menjaga agar kode kita tetap bersih dan terstruktur. Di bagian ini, kita akan
membahas beberapa kustomisasi yang populer untuk menyesuaikan Form Request agar lebih
powerful dan relevan dengan konteks aplikasi kita

Custom error message

Secara default Laravel menyediakan pesan error yang cukup informatif, tapi terkadang ku-
rang sesuai dengan bahasa yang kita gunakan. Nah, dengan method messages(), kita bisa
mengganti pesan-pesan error ini dengan versi sesuai dengan kenginan kita. Caranya cukup
dengan mengembalikan array yang berisi aturan validasi sebagai key dan pesan yang ingin
ditampilkan sebagai value.

public function messages(): array


{
return [
'email.required' => 'Alamat email wajib diisi.',
'email.email' => 'Format email tidak valid.',
'amount.required' => 'Nominal donasi wajib diisi.',
'amount.min' => 'Minimal donasi adalah Rp10.000.',
'payment_method.in' => 'Metode pembayaran tidak valid.',
];
}

Dengan begini, pesan error yang muncul saat validasi gagal akan lebih baik, hal ini yang
akan membantu pengguna aplikasi supaya bisa memperbaiki data yang dikirimkannya pada
sistem.

Menggubah nama Atribut

Selain isi pesan error, kita juga bisa mengganti nama field yang ditampilkan di dalam pesan
error. Laravel secara default akan menampilkan nama field sesuai yang tertulis di form —
misalnya payment_method. Tapi bagi user awam, nama seperti itu bisa membingungkan.
maka dari itu kita akan menggunakan attributes() untuk melakukannya, contohnya sebagai
berikut:

public function attributes(): array


{
return [
'name' => 'nama lengkap',
'email' => 'alamat email',
'amount' => 'nominal donasi',

83
'message' => 'pesan dukungan',
'payment_method' => 'metode pembayaran',
];
}

Setelah membuat method attributes(), jika ada error pada field payment_method, user
akan melihat pesan seperti “metode pembayaran wajib diisi”, bukan “payment_method is
required”.
Dan masih banyak lagi kustomisasi yang bisa kalian lakukan, untuk lebih lenmgkap-nya kalian
bisa membaca pada halaman validation di Laravel documentation

84

Anda mungkin juga menyukai