Laravel Collections Macros Higher Order Helpers Pipeline
Definisi
Illuminate\Support\Collection adalah pembungkus array di Laravel yang membawa puluhan method chaining untuk transformasi data. collect($array) membuat instance baru. Sebagian besar method mengembalikan Collection baru, bukan mengubah yang lama.
LazyCollection adalah varian yang menunda evaluasi dan memproses item satu per satu lewat generator — cocok untuk dataset besar yang tidak boleh dimuat ke memory sekaligus.
Collection::macro() memperluas Collection dengan method custom tanpa subclass, lewat trait Macroable yang sudah disematkan di kelas Collection.
Kapan dipakai
- Memproses array atau hasil query jadi alur berantai (
map→filter→reduce). - Mengganti loop
foreach+ array PHP mentah dengan operasi deklaratif yang mudah diraitakan. - Membaca file besar, cursor database, atau stream tanpa membebani memory (
LazyCollection). - Menambah helper berulang (misal
->toUpper()) ke semua Collection di project viamacro().
Cara kerja: eager vs lazy
Collection biasa (eager) mengevaluasi seluruh item saat method dipanggil dan menyimpan hasil di memory. LazyCollection mengevaluasi item saat dibutuhkan (lazy), sehingga map/filter tidak memicu iterasi penuh sampai ada konsumsi seperti each(), toArray(), first(), atau take() yang menyentuh item.
use Illuminate\Support\Collection;
use Illuminate\Support\LazyCollection;
// Eager: semua item langsung diproses & disimpan di memory
$hasil = collect([1, 2, 3, 4])
->map(fn ($x) => $x * 2)
->filter(fn ($x) => $x > 4)
->values();
// Lazy: diproses satu per satu saat dikonsumsi
$stream = LazyCollection::make(function () {
$handle = fopen('log-besar.txt', 'r');
while (($line = fgets($handle)) !== false) {
yield $line;
}
})
->filter(fn ($line) => str_contains($line, 'ERROR'))
->take(10); // cuma 10 baris ERROR yang benar-benar dibaca dari diskHigher-Order Helpers: map / filter / reduce
map — transformasi tiap item
map memanggil callback untuk tiap item dan mengembalikan Collection baru berisi hasil callback. Collection asli tidak berubah.
$angka = collect([1, 2, 3]);
$double = $angka->map(fn ($x) => $x * 2); // [2, 4, 6]
// $angka tetap [1, 2, 3]filter — saring item
filter menyimpan item di mana callback bernilai truthy. reject adalah kebalikannya. Keduanya mengembalikan Collection baru.
$aktif = $users->filter(fn ($u) => $u->active);
$nonaktif = $users->reject(fn ($u) => $u->active);reduce — akumulasi jadi satu nilai
reduce menjumlahkan/menggabungkan item jadi nilai tunggal (skalar atau tipe apa pun yang dikembalikan callback), bukan Collection.
$total = collect([1, 2, 3])->reduce(
fn ($carry, $item) => $carry + $item,
0
); // 6, bukan CollectionHelper fluent lain
pipe(callable)— teruskan Collection ke callable dan kembalikan nilai apapun yang dikembalikan callable (terminal, sepertireducetapi bebas bentuk).tap(callable)— jalankan side-effect (logging, assert) lalu kembalikan Collection aslinya tanpa diubah.
$out = collect([1, 2, 3])
->pipe(fn ($c) => $c->sum() * 2); // 12
collect([1, 2, 3])
->tap(fn ($c) => logger("count: {$c->count()}"));Higher-Order Messages (proxy)
Laravel punya higher-order proxy: map, filter, reject, each, groupBy, sortBy, dsb. bisa dipanggil tanpa closure bila yang diinginkan adalah memanggil method/property tiap item.
// setara dengan $users->map(fn ($u) => $u->name)
$nama = $users->map->name;
// saring item yang property/method aktif bernilai truthy
$aktif = $users->filter->active;
// panggil method tiap item (side-effect)
$users->each->notify();Macro: memperluas Collection
Daftarkan macro di boot() service provider (biasanya AppServiceProvider). Di dalam closure, $this merujuk ke instance Collection yang sedang dipanggil.
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Collection::macro('toUpper', function () {
return $this->map(fn ($value) => is_string($value)
? strtoupper($value)
: $value);
});
// Daftarkan banyak macro sekaligus lewat mixin
// (tiap method public di class harus mengembalikan closure)
Collection::mixin(new \App\Support\CollectionMacros);
}
}collect(['a', 'b'])->toUpper(); // ['A', 'B']Macro tersedia di semua Collection di aplikasi setelah didaftarkan. Jangan mendeklarasikan Collection::macro() di dalam controller/closure yang bisa berjalan berulang — deklarasi ganda memicu exception "macro already exists".
Alur pipeline collection
Catatan alur: map/filter/reject selalu mengembalikan Collection baru (immutable), sedangkan reduce/sum/max mengembalikan skalar. Jangan panggil method Collection lagi di atas hasil reduce.
Jebakan mutasi collection
-
Chaining tidak mengubah aslinya.
map/filter/reject/slicemengembalikan instance baru. Lupa menampung hasilnya = perubahan hilang.php
$c = collect([1, 2, 3]); $c->map(fn ($x) => $x * 2); // hasil dibuang // $c tetap [1, 2, 3] $c = $c->map(fn ($x) => $x * 2); // benar -
Object di dalam tetap referensi yang sama.
mapmengembalikan Collection baru, tapi bila item berupa object, ia masih menunjuk object asli. Mutasi object di dalam callback tetap mengubah object asli.php
$users->map(function ($u) { $u->name = strtoupper($u->name); // mengubah object asli! return $u; }); -
transformitu mutating.transform()memodifikasi Collection di tempat (in-place), berbeda denganmapyang mengembalikan salinan.php
$c = collect([1, 2, 3]); $c->transform(fn ($x) => $x * 2); // $c sekarang [2, 4, 6] -
forget/push/put/splicememodifikasi instance. Method ini mutating, bukan mengembalikan salinan.php
$c = collect(['a' => 1, 'b' => 2]); $c->forget('b'); // $c langsung berubah jadi ['a' => 1] -
Key numerik di-reset oleh
values().filter/mapmempertahankan key, jadi hasil bisa punya gap index.php
collect([1, 2, 3, 4]) ->filter(fn ($x) => $x > 2); // [2 => 3, 3 => 4] // pakai ->values() kalau butuh index rapat 0, 1, ... -
reducehasilnya bukan Collection. Memanggil->map()di atas hasilreduceakan error karena skalar tidak punya method itu.
Kesalahan umum
- Mengharapkan
collect()eager memproses jutaan baris (pakaiLazyCollection+cursor()dari Eloquent untuk hindari memory spike). - Mencampur
mapdantransformtanpa sadar sehingga muncul side-effect tak terduga. - Mendeklarasikan
Collection::macro()di dalam closure yang dieksekusi berulang (bootstrap duplikat → exception). - Lupa
values()setelahfilterlalu mengandalkan index berurut di JSON/loop berbasis angka.