Cara Menggunakan Laravel Collection Secara Efektif

Laravel Collection adalah salah satu fitur paling powerful yang sering diremehkan oleh developer pemula. Collection adalah pembungkus (wrapper) yang elegan untuk bekerja dengan arr...

Cara Menggunakan Laravel Collection Secara Efektif
Iklan

Laravel Collection adalah salah satu fitur paling powerful yang sering diremehkan oleh developer pemula. Collection adalah pembungkus (wrapper) yang elegan untuk bekerja dengan array data, menyediakan puluhan method bawaan yang memudahkan manipulasi data tanpa perlu menulis loop manual. Setelah membaca artikel ini, kamu akan menggunakan Collection secara lebih cerdas dan efisien.

Di artikel ini kita bahas dari dasar: apa itu Collection, method yang paling sering dipakai (map, filter, groupBy, pluck, reduce), teknik chaining, lalu masuk ke hal yang jarang dibahas tutorial lain: perbedaan Collection biasa dengan Eloquent Collection, kapan sebaiknya memfilter di database daripada di Collection, studi kasus laporan penjualan, kesalahan umum, dan tips performa untuk data besar. Semua contoh kode bisa langsung kamu coba di php artisan tinker pada Laravel 10, 11, maupun 12.

Apa itu Laravel Collection?

Collection adalah instance dari kelas Illuminate\Support\Collection. Setiap query Eloquent yang mengembalikan banyak baris akan otomatis menjadi Collection. Kamu juga bisa membuat Collection secara manual:

Iklan
<?php

use Illuminate\Support\Collection;

// Dari array biasa
$collection = collect([1, 2, 3, 4, 5]);

// Dari array asosiatif
$users = collect([
    ['name' => 'Budi', 'age' => 25, 'city' => 'Jakarta'],
    ['name' => 'Ani',  'age' => 30, 'city' => 'Bandung'],
    ['name' => 'Caca', 'age' => 22, 'city' => 'Jakarta'],
    ['name' => 'Dodi', 'age' => 28, 'city' => 'Surabaya'],
]);

Helper collect() hanyalah jalan pintas untuk new Collection($array). Variabel $users di atas akan kita pakai di hampir semua contoh berikutnya, jadi simpan baik-baik.

Kenapa tidak pakai array biasa saja?

Secara teknis semua yang dilakukan Collection bisa kamu tulis dengan fungsi array PHP seperti array_map, array_filter, dan foreach. Masalahnya, fungsi array bawaan PHP punya urutan parameter yang tidak konsisten (array_map menerima callback di depan, array_filter di belakang) dan tidak bisa dirangkai. Hasilnya kode jadi penuh variabel sementara. Kalau dasar array PHP kamu masih belum mantap, baca dulu cara menggunakan array di PHP (associative dan multidimensi), karena Collection pada dasarnya membungkus struktur yang sama.

Bandingkan dua versi berikut yang melakukan hal yang sama, yaitu mengambil nama user berusia di atas 24 dalam huruf besar:

// Versi array biasa
$result = [];
foreach ($data as $user) {
    if ($user['age'] > 24) {
        $result[] = strtoupper($user['name']);
    }
}

// Versi Collection
$result = collect($data)
    ->filter(fn ($user) => $user['age'] > 24)
    ->map(fn ($user) => strtoupper($user['name']))
    ->values();

Versi Collection dibaca seperti kalimat: saring, ubah, rapikan index. Saat logikanya bertambah, perbedaan keterbacaannya makin terasa.

Iklan

Collection bersifat immutable (hampir selalu)

Satu konsep penting: sebagian besar method Collection tidak mengubah Collection asli, melainkan mengembalikan Collection baru. Jadi $users->filter(...) tidak membuat $users berkurang isinya. Kamu harus menampung hasilnya ke variabel. Pengecualiannya ada beberapa method seperti transform(), push(), put(), pull(), forget(), pop(), dan shift() yang memang mengubah Collection itu sendiri.

Method map(): Transformasi Data

map() mengiterasi setiap item dan mengembalikan Collection baru hasil transformasi:

// Mengubah semua nama menjadi huruf besar
$upperNames = $users->map(function ($user) {
    return strtoupper($user['name']);
});
// Result: ['BUDI', 'ANI', 'CACA', 'DODI']

// Dengan arrow function (PHP 7.4+)
$prices = collect([10000, 25000, 15000]);
$discounted = $prices->map(fn($price) => $price * 0.9);
// Result: [9000.0, 22500.0, 13500.0] (float, karena dikali 0.9)

Callback map() juga menerima argumen kedua berupa key. Ini berguna ketika kamu butuh nomor urut atau key asosiatif:

$labels = $users->map(fn ($user, $index) => ($index + 1) . '. ' . $user['name']);
// Result: ['1. Budi', '2. Ani', '3. Caca', '4. Dodi']

Kalau kamu ingin hasilnya berupa pasangan key-value baru, gunakan mapWithKeys(). Callback-nya harus mengembalikan array berisi satu pasangan:

Iklan
$ageByName = $users->mapWithKeys(fn ($user) => [$user['name'] => $user['age']]);
// Result: ['Budi' => 25, 'Ani' => 30, 'Caca' => 22, 'Dodi' => 28]

Method filter(): Menyaring Data

filter() mengembalikan Collection yang hanya berisi item yang lulus kondisi:

// Filter user dari Jakarta
$jakartaUsers = $users->filter(fn($user) => $user['city'] === 'Jakarta');
// Result: Budi dan Caca

// Filter tanpa callback: hapus nilai falsy (null, false, 0, '')
$clean = collect([1, null, 2, false, 3, 0])->filter();
// Result: [1, 2, 3] (key asli tetap: 0, 2, 4)

// Re-index setelah filter
$reindexed = $jakartaUsers->values();

Perhatikan bahwa filter() mempertahankan key asli. Caca ada di index 2, jadi setelah difilter key-nya tetap 2, bukan 1. Itu sebabnya values() sering dipasang di akhir. Kebalikan dari filter() adalah reject(), yang membuang item yang memenuhi kondisi.

where(), whereIn(), dan firstWhere()

Untuk kondisi sederhana, kamu tidak perlu menulis callback. Collection punya method bergaya query builder:

$users->where('city', 'Jakarta');            // Budi, Caca
$users->where('age', '>', 25);               // Ani, Dodi
$users->whereIn('city', ['Bandung', 'Surabaya']); // Ani, Dodi
$users->whereBetween('age', [23, 29]);       // Budi, Dodi
$users->firstWhere('city', 'Jakarta');       // ['name' => 'Budi', ...]

Ingat, method ini berjalan di memori PHP, bukan di database. Bedanya akan kita bahas di bagian kesalahan umum.

Iklan

partition(): memecah jadi dua kelompok sekaligus

[$dewasa, $muda] = $users->partition(fn ($user) => $user['age'] >= 25);
// $dewasa: Budi, Ani, Dodi
// $muda:   Caca

Ini lebih rapi daripada memanggil filter() lalu reject() dengan kondisi yang sama.

Method groupBy(): Mengelompokkan Data

groupBy() sangat berguna untuk mengelompokkan data berdasarkan key tertentu:

$byCity = $users->groupBy('city');

/*
Result:
[
  'Jakarta'  => [Budi, Caca],
  'Bandung'  => [Ani],
  'Surabaya' => [Dodi],
]
*/

// Akses tiap group
$jakartaGroup = $byCity->get('Jakarta');
$jakartaGroup->count(); // 2

Setiap grup adalah Collection juga, jadi kamu bisa langsung merangkai method lain. Misalnya menghitung rata-rata usia per kota:

$avgAgeByCity = $users
    ->groupBy('city')
    ->map(fn ($group) => $group->avg('age'));
// Result: ['Jakarta' => 23.5, 'Bandung' => 30, 'Surabaya' => 28]

// Kalau hanya butuh jumlah per kelompok, countBy() lebih singkat
$users->countBy('city');
// Result: ['Jakarta' => 2, 'Bandung' => 1, 'Surabaya' => 1]

groupBy() juga menerima callback, sehingga kamu bisa mengelompokkan berdasarkan nilai turunan, misalnya rentang usia atau bulan dari sebuah tanggal. Contohnya ada di studi kasus di bawah.

Iklan

Method pluck(): Mengambil Nilai Satu Kolom

// Ambil semua nama
$names = $users->pluck('name');
// Result: ['Budi', 'Ani', 'Caca', 'Dodi']

// Buat key-value pair
$nameById = User::all()->pluck('name', 'id');
// Result: [1 => 'Budi', 2 => 'Ani', ...]

Hasil pluck('name', 'id') sangat berguna untuk dropdown select di Blade:

<select name="user_id">
    @foreach($nameById as $id => $name)
        <option value="{{ $id }}">{{ $name }}</option>
    @endforeach
</select>

Tips: kalau kamu hanya butuh dua kolom dari database, lebih hemat memanggil User::pluck('name', 'id') langsung di query builder daripada User::all()->pluck(...). Versi pertama hanya mengambil kolom name dan id dari database, sedangkan versi kedua mengambil semua kolom dan membuat objek model untuk setiap baris.

Method yang mirip pluck() adalah keyBy(). Bedanya, keyBy('id') mempertahankan seluruh item tetapi menjadikan kolom tertentu sebagai key, sehingga pencarian berdasarkan ID cukup dengan $collection->get($id) tanpa perlu loop.

Method reduce(): Akumulasi Nilai

// Hitung total harga
$items = collect([
    ['name' => 'Buku', 'price' => 50000, 'qty' => 2],
    ['name' => 'Pensil', 'price' => 5000, 'qty' => 5],
    ['name' => 'Tas', 'price' => 200000, 'qty' => 1],
]);

$total = $items->reduce(function ($carry, $item) {
    return $carry + ($item['price'] * $item['qty']);
}, 0);
// Result: 325000

// Untuk kasus sesederhana ini, sum() dengan callback lebih ringkas
$total = $items->sum(fn ($item) => $item['price'] * $item['qty']);
// Result: 325000

Jangan lupa argumen kedua reduce() (nilai awal 0). Tanpa itu, nilai awal $carry adalah null. Untuk penjumlahan memang tetap jalan, tapi untuk akumulasi berupa array atau string hasilnya bisa tidak sesuai harapan. reduce() paling cocok ketika hasil akhirnya bukan sekadar angka, misalnya membangun array ringkasan atau string.

Iklan

Chaining Method: Kekuatan Sebenarnya Collection

Keunggulan terbesar Collection adalah kemampuan method chaining yang membuat kode lebih ekspresif:

// Ambil nama user dari Jakarta, usia di atas 23, diurutkan abjad
$result = $users
    ->filter(fn($u) => $u['city'] === 'Jakarta')
    ->filter(fn($u) => $u['age'] > 23)
    ->sortBy('name')
    ->pluck('name')
    ->values();
// Result: ['Budi']

Beberapa method membantu chaining tetap rapi meski ada kondisi:

$kota = request('city'); // bisa null

$result = $users
    ->when($kota, fn ($c) => $c->where('city', $kota))
    ->sortBy([
        ['city', 'asc'],
        ['age', 'desc'],
    ])
    ->values();

// pipe() meneruskan seluruh Collection ke callback dan mengembalikan hasilnya
$ringkasan = $users->pipe(fn ($c) => [
    'total'    => $c->count(),
    'rata_usia' => $c->avg('age'),
]);
// Result: ['total' => 4, 'rata_usia' => 26.25]

when() hanya menjalankan callback jika nilai pertama truthy, mirip dengan when() di query builder. sortBy() dengan array berisi pasangan kolom dan arah memungkinkan pengurutan bertingkat. Saat debugging rantai yang panjang, sisipkan ->dump() di tengah rantai untuk melihat isi Collection pada titik itu tanpa memutus alurnya.

Method Berguna Lainnya

  • sum('price'): menjumlahkan nilai dari key tertentu.
  • avg('age'): rata-rata nilai.
  • max('age') / min('age'): nilai maksimum/minimum.
  • sortBy('name') / sortByDesc('age'): mengurutkan data (key asli dipertahankan).
  • unique('city'): menghapus duplikat.
  • chunk(3): memecah Collection menjadi potongan-potongan kecil.
  • contains('name', 'Budi'): cek apakah ada item yang cocok.
  • first() / last(): mengambil elemen pertama/terakhir.
  • take(3): mengambil sejumlah item dari depan (angka negatif dari belakang).
  • flatMap(): seperti map() lalu meratakan hasilnya satu tingkat.
  • isEmpty() / isNotEmpty(): cek apakah Collection kosong.
  • toArray(): mengubah kembali menjadi array PHP biasa.
  • toJson(): mengubah menjadi string JSON.

Tabel ringkas: pilih method yang tepat

KebutuhanMethodCatatan
Mengubah setiap itemmap()Mengembalikan Collection baru
Mengubah item di tempattransform()Mengubah Collection asli
Menjalankan aksi tanpa hasil barueach()Return false untuk berhenti
Menyaring itemfilter(), where(), reject()Key asli tetap, pakai values()
Satu kolom sajapluck()Bisa jadi key-value
MengelompokkangroupBy(), countBy()Tiap grup juga Collection
Menghitung satu nilai akhirsum(), avg(), reduce()reduce() untuk logika bebas
Data sangat besarLazyCollection, cursor(), lazy()Hemat memori

Collection Biasa vs Eloquent Collection

Hasil User::all() atau User::where(...)->get() sebenarnya adalah Illuminate\Database\Eloquent\Collection, turunan dari Collection biasa. Semua method di atas tetap bisa dipakai, ditambah beberapa method khusus model:

Iklan
$users = User::where('active', true)->get();

$users->find(3);             // cari model dengan primary key 3 di dalam Collection
$users->modelKeys();         // [1, 2, 3, ...] daftar primary key
$users->load('posts');       // eager load relasi setelah data diambil
$users->loadCount('posts');  // tambah atribut posts_count
$users->fresh();             // ambil ulang data terbaru dari database

Ada satu perilaku yang sering membingungkan: jika map() pada Eloquent Collection mengembalikan sesuatu yang bukan model (misalnya array atau string), hasilnya otomatis menjadi Collection biasa. Jadi method khusus seperti load() tidak lagi tersedia setelah itu. Urutkan rantai kamu supaya operasi khusus model dilakukan lebih dulu.

Dapatkan Update Terbaru

Berlangganan gratis. Artikel & tutorial coding terbaru langsung ke email kamu. Tanpa spam.

Method load() di atas adalah senjata utama melawan masalah N+1 query. Kalau kamu belum familiar dengan relasi antar model, pelajari dulu Eloquent Relationship one to many dan many to many, karena relasi dan Collection hampir selalu dipakai bersamaan.

Studi Kasus: Laporan Penjualan per Bulan

Mari gabungkan semuanya ke kasus nyata. Kita punya data pesanan dan ingin membuat ringkasan omzet per bulan (hanya pesanan berstatus paid) serta mencari pelanggan dengan belanja terbesar.

$orders = collect([
    ['id' => 1, 'date' => '2026-01-05', 'customer' => 'Budi', 'total' => 150000, 'status' => 'paid'],
    ['id' => 2, 'date' => '2026-01-18', 'customer' => 'Ani',  'total' => 90000,  'status' => 'paid'],
    ['id' => 3, 'date' => '2026-01-20', 'customer' => 'Budi', 'total' => 60000,  'status' => 'cancelled'],
    ['id' => 4, 'date' => '2026-02-02', 'customer' => 'Caca', 'total' => 200000, 'status' => 'paid'],
    ['id' => 5, 'date' => '2026-02-14', 'customer' => 'Ani',  'total' => 120000, 'status' => 'paid'],
]);

$paid = $orders->where('status', 'paid');

// 1. Omzet per bulan
$perBulan = $paid
    ->groupBy(fn ($order) => substr($order['date'], 0, 7)) // '2026-01'
    ->map(fn ($group, $bulan) => [
        'bulan'        => $bulan,
        'jumlah_order' => $group->count(),
        'omzet'        => $group->sum('total'),
    ])
    ->values();
/*
[
  ['bulan' => '2026-01', 'jumlah_order' => 2, 'omzet' => 240000],
  ['bulan' => '2026-02', 'jumlah_order' => 2, 'omzet' => 320000],
]
*/

// 2. Pelanggan dengan belanja terbesar
$topCustomer = $paid
    ->groupBy('customer')
    ->map(fn ($group) => $group->sum('total'))
    ->sortDesc()
    ->keys()
    ->first();
// Result: 'Ani' (90000 + 120000 = 210000)

// 3. Total omzet keseluruhan
$grandTotal = $paid->sum('total'); // 560000

Tanpa Collection, kode di atas butuh beberapa foreach bersarang dan array penampung. Dengan Collection, setiap langkah jelas maksudnya. Pola groupBy lalu map lalu values ini akan sering kamu pakai untuk dashboard, laporan, dan data grafik.

Iklan

Catatan penting: kalau data pesanan ada di database dan jumlahnya ribuan baris, laporan seperti ini lebih efisien dihitung dengan GROUP BY dan SUM() di SQL. Collection cocok untuk data yang sudah ada di memori atau jumlahnya wajar.

Kesalahan Umum Saat Memakai Collection

1. Memfilter di Collection padahal bisa di database

// Kurang tepat: mengambil SEMUA user, lalu menyaring di PHP
$admins = User::all()->where('role', 'admin');

// Benar: biarkan database yang menyaring
$admins = User::where('role', 'admin')->get();

Keduanya terlihat mirip karena nama method-nya sama, tapi versi pertama memuat seluruh tabel ke memori. Di tabel berisi puluhan ribu baris, ini bisa membuat halaman lambat atau kehabisan memori. Aturan praktisnya: saring, urutkan, dan batasi data di query; gunakan Collection untuk mengolah hasil yang sudah diambil. Teknik lain untuk mempercepat aplikasi dibahas di 8 cara optimasi performa Laravel.

2. Lupa values() sehingga JSON berubah jadi object

Setelah filter(), where(), atau sortBy(), key bisa tidak berurutan (misalnya 0, 2, 3). Saat dikirim sebagai JSON, array dengan key tidak berurutan akan menjadi object {"0":...,"2":...}, bukan array [...]. Di frontend, kode seperti data.map() di JavaScript langsung error. Solusinya cukup tambahkan ->values() sebelum mengembalikan response.

3. Mengira method Collection mengubah data asli

$users->sortBy('name');      // hasilnya dibuang, $users tidak berubah
$sorted = $users->sortBy('name'); // benar

4. Memakai map() untuk efek samping

Jika kamu hanya ingin menjalankan aksi (mengirim email, menulis log) tanpa butuh hasil baru, gunakan each(). map() dimaksudkan untuk menghasilkan Collection baru, dan memakainya untuk efek samping membuat kode menyesatkan bagi pembaca berikutnya.

Iklan

5. Query di dalam loop Collection (N+1)

// Buruk: 1 query user + 1 query per user untuk posts
$users = User::all();
$users->each(fn ($user) => logger($user->posts->count()));

// Baik: eager load sekali di awal
$users = User::withCount('posts')->get();
$users->each(fn ($user) => logger($user->posts_count));

Lazy Collection untuk Data Besar

Jika kamu perlu memproses data yang sangat besar (ratusan ribu baris), gunakan LazyCollection agar memori tetap efisien. LazyCollection memakai generator PHP, sehingga item dibaca satu per satu saat dibutuhkan, bukan dimuat semuanya sekaligus:

use Illuminate\Support\LazyCollection;

// Proses file besar baris per baris
LazyCollection::make(function () {
    $handle = fopen(storage_path('app/data.csv'), 'r');
    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
    fclose($handle);
})->chunk(1000)->each(function ($chunk) {
    // Proses per 1000 baris
});

Untuk data dari database, Eloquent punya dua cara yang mengembalikan LazyCollection:

// cursor(): satu query, model dibuat satu per satu saat diiterasi
foreach (User::where('active', true)->cursor() as $user) {
    // proses $user
}

// lazy(): mengambil data per potongan (default 1000 baris) di balik layar
User::where('active', true)->lazy()->each(function ($user) {
    // proses $user
});

Perbedaannya, cursor() hanya menjalankan satu query tetapi tidak bisa memakai eager loading, sedangkan lazy() menjalankan beberapa query berdasarkan potongan data. Kalau kamu juga mengubah data di dalam loop, pertimbangkan lazyById() agar potongan data tidak bergeser. Proses berat seperti ini biasanya dijalankan lewat artisan command atau scheduler, bukan saat request pengguna.

Memperluas Collection dengan Macro

Jika ada rangkaian method yang terus kamu ulang di banyak tempat, daftarkan sebagai macro di method boot() pada AppServiceProvider:

Iklan
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

public function boot(): void
{
    Collection::macro('toUpper', function () {
        return $this->map(fn ($value) => Str::upper($value));
    });
}

// Pemakaian di mana saja
collect(['budi', 'ani'])->toUpper(); // ['BUDI', 'ANI']

Gunakan macro secukupnya. Terlalu banyak macro membuat kode sulit dipahami developer lain yang belum tahu macro itu ada.

Kesimpulan

Laravel Collection mengubah cara kamu berpikir tentang manipulasi data. Daripada menulis loop foreach bertumpuk, gunakan method Collection yang ekspresif dan bisa di-chain. Mulai dari map(), filter(), groupBy(), hingga reduce(), semua tersedia siap pakai. Ingat tiga prinsip utamanya: hasil method adalah Collection baru, saring data sebanyak mungkin di database sebelum masuk Collection, dan pakai LazyCollection untuk data yang sangat besar. Semakin kamu familiar dengan Collection, semakin bersih dan mudah dibaca kode Laravel-mu. Kalau ingin melihat Collection dalam konteks proyek yang utuh, lanjutkan ke panduan belajar Laravel dari nol sampai mahir.

FAQ

Apa beda map() dan each() di Laravel Collection?

map() mengembalikan Collection baru berisi hasil callback, sedangkan each() hanya menjalankan callback untuk setiap item dan mengembalikan Collection asli. Pakai map() saat butuh data hasil transformasi, dan each() saat hanya butuh efek samping seperti menyimpan atau mengirim notifikasi.

Kenapa hasil filter() jadi object saat dikirim sebagai JSON?

Karena filter() mempertahankan key asli sehingga key-nya tidak lagi berurutan dari 0. JSON hanya bisa merepresentasikan array dengan key berurutan; selain itu menjadi object. Tambahkan ->values() untuk mereset key.

Iklan

Lebih baik User::all()->where() atau User::where()->get()?

Hampir selalu User::where()->get(), karena penyaringan terjadi di database dan hanya baris yang dibutuhkan yang dimuat ke memori. all()->where() hanya masuk akal jika datanya memang sedikit atau sudah terlanjur diambil untuk keperluan lain.

Kapan harus memakai LazyCollection?

Saat jumlah data terlalu besar untuk dimuat sekaligus, misalnya membaca file CSV besar, mengekspor seluruh tabel, atau memproses ulang data lama lewat artisan command. Untuk halaman web biasa dengan data yang sudah dipaginasi, Collection biasa sudah cukup.

Bagaimana cara mengubah Collection kembali menjadi array?

Gunakan toArray(). Method ini juga mengubah model Eloquent di dalamnya menjadi array. Jika kamu hanya ingin array mentah tanpa konversi item, gunakan all().

Iklan
laravel collection collection laravel method collection laravel map filter laravel laravel eloquent collection manipulasi data laravel
Rekomendasi

Hosting Cepat untuk Website & Laravel

Butuh hosting yang ngebut dan stabil untuk deploy website atau aplikasi Laravel-mu? Ini rekomendasi yang saya pakai.

Lihat Rekomendasi Hosting
Yudhi
Ditulis oleh

Yudhi

Web Developer

Web developer yang sehari-hari berkutat dengan PHP, Laravel, JavaScript, dan MySQL. Terbiasa membangun aplikasi web dari nol โ€” merancang database, menulis fitur, memburu bug, hingga deploy ke server โ€” lalu menuangkan solusi dan tutorialnya di DhieCoderWeb agar lebih mudah diikuti developer lain.

Bagikan artikel
Kembali

Komentar (0)

Punya pertanyaan atau tambahan? Tulis di bawah โ€” tak perlu login.

Membalas komentarโ€ฆ batal

Belum ada komentar. Jadilah yang pertama!

๐Ÿš€ Partner Recommendation

Butuh Source Code & Aplikasi Premium?

Download aplikasi Laravel, POS, Sekolah, Klinik, ERP, dan source code siap pakai di GudangCode.

GudangCode
  • โœ” Source Code Premium
  • โœ” Sistem Siap Pakai
  • โœ” Lifetime Update
  • โœ” Membership Lifetime
  • โœ” Update Aplikasi Harian
Daftar Membership โ†’
Iklan
Iklan