Data mentah yang tiba dari berkas CSV, respons API, atau hasil query database hampir tidak pernah berbentuk rapi: ada spasi berlebih pada nama produk, angka tersimpan sebagai teks, dan nama kolom yang berbeda antar sistem. Data transformation adalah proses mengubah data dari bentuk aslinya menjadi bentuk yang dibutuhkan aplikasi — bersih, bertipe data yang benar, dan tersusun rapi. Chapter ini menyusun kotak peralatan transformasi yang paling sering dipakai: normalisasi nilai, koersi tipe, pengubahan nama kunci, perataan dan pengelompokan struktur, pengayaan dengan data referensi, hingga pemformatan hasil akhir. Seluruh contoh memakai array PHP sebagai bahan baku karena data dari format apa pun — CSV, JSON, maupun database — pada akhirnya diwakili sebagai array asosiatif setelah dibaca.
Normalisasi Nilai #
Normalisasi bertujuan menyamakan bentuk nilai sehingga perbandingan, pencarian, dan penggabungan data bekerja konsisten. String " Budi " dan "budi" hanya dianggap sama setelah spasi tepi dihapus dan kapitalisasi diseragamkan; tanpa normalisasi, keduanya menjadi dua entitas berbeda pada laporan. Tiga fungsi menjadi tulang punggung tahap ini: trim() menghapus spasi dan baris baru di awal serta akhir string, mb_strtolower() menyeragamkan huruf kecil, dan mb_convert_case() dengan mode MB_CASE_TITLE membangun kapitalisasi judul yang rapi.
Fungsi-fungsi tersebut paling sering dipadukan lewat array_map(), yang menerapkan satu fungsi ke setiap elemen array dan mengembalikan array hasil transformasi:
<?php
$produkMentah = [
["nama" => " kopi arabika ", "kategori" => "MINUMAN"],
["nama" => "TEH HIJAU", "kategori" => "minuman "],
["nama" => " gula aren", "kategori" => "Bahan Pokok"],
];
$produkBersih = array_map(
fn (array $baris): array => [
"nama" => mb_convert_case(trim($baris["nama"]), MB_CASE_TITLE),
"kategori" => mb_strtolower(trim($baris["kategori"])),
],
$produkMentah
);
foreach ($produkBersih as $produk) {
printf("%s — kategori: %s\n", $produk["nama"], $produk["kategori"]);
}Output yang Dihasilkan:
Kopi Arabika — kategori: minuman
Teh Hijau — kategori: minuman
Gula Aren — kategori: bahan pokokMari kita telaah baris-baris penting pada contoh di atas:
- Closure
fn (array $baris): array: menerima satu baris array asosiatif dan wajib mengembalikan array baru;array_map()memanggil closure ini untuk ketiga elemen$produkMentahsecara berurutan. trim($baris["nama"]): menghapus spasi di tepi sebelum kapitalisasi diterapkan — urutan ini penting agar spasi tidak “dilindungi” di dalam kata.mb_convert_case(..., MB_CASE_TITLE): mengubah" kopi arabika "menjadi"Kopi Arabika"dengan menaikkan huruf pertama setiap kata.- Perulangan
foreachterakhir: mencetak hasil untuk menunjukkan bahwa ketiga baris kini mengikuti satu bentuk standar.
Koersi Tipe saat Transformasi #
PHP adalah bahasa dinamis: string "5" dan integer 5 dapat saling menggantikan pada banyak operasi, tetapi kenyamanan ini sering menutupi masalah. Sumber data eksturnal hampir selalu mengirim angka sebagai teks — kolom hasil fgetcsv(), nilai form HTTP, hingga field JSON tertentu. Koersi eksplisit (type cast) membuat maksud kode jelas sejak ditulis dan menghindari kejutan seperti penjumlahan string yang sebenarnya ingin menjumlahkan angka.
<?php
$barisCsv = ["jumlah" => "5", "harga" => "12500.50", "aktif" => "1", "catatan" => ""];
var_dump(is_string($barisCsv["jumlah"]));
$jumlah = (int) $barisCsv["jumlah"];
$harga = (float) $barisCsv["harga"];
$aktif = filter_var($barisCsv["aktif"], FILTER_VALIDATE_BOOL);
var_dump($jumlah);
var_dump($harga);
var_dump($aktif);Output yang Dihasilkan:
bool(true)
int(5)
float(12500.5)
bool(true)Cast (int) dan (float) mengubah string numerik menjadi angka sejati — perhatikan bahwa var_dump() kini melaporkan int(5) dan float(12500.5), bukan string. Untuk boolean, "1" dan "0" tidak boleh dibiarkan mengalir sebagai string karena keduanya sama-sama dianggap true saat dipakai sebagai boolean; filter_var() dengan FILTER_VALIDATE_BOOL menerjemahkan "1", "true", dan "on" menjadi true, selebihnya false. Setiap koersi sebaiknya ditempatkan di satu tempat — tepat setelah data dibaca — agar seluruh kode di hilir bekerja dengan tipe yang sudah pasti.
Mengubah Nama Kunci #
Saat menggabungkan data dari dua sistem, misalnya basis internal dengan kolom nama_barang dan API mitra dengan kolom productName, nama kunci perlu dipetakan ke satu standar. PHP tidak menyediakan fungsi bawaan untuk mengganti nama kunci array secara massal, tetapi pola berikut melakukannya dengan aman:
<?php
$namaKolom = [
"nama_barang" => "productName",
"harga_satuan" => "unitPrice",
];
$baris = ["nama_barang" => "kopi arabika", "harga_satuan" => 85000];
$ubahKunci = function (array $baris) use ($namaKolom): array {
$hasil = [];
foreach ($baris as $kunci => $nilai) {
$hasil[$namaKolom[$kunci] ?? $kunci] = $nilai;
}
return $hasil;
};
echo json_encode($ubahKunci($baris)), "\n";Output yang Dihasilkan:
{"productName":"kopi arabika","unitPrice":85000}Closure $ubahKunci membangun array baru dari array lama: setiap kunci dicari pemetaannya di $namaKolom, dan operator ?? memastikan kunci tanpa pemetaan tetap lolos dengan nama aslinya. Pola ini lebih aman daripada unset() lalu menambah kunci baru satu per satu karena nilai tidak pernah sempat “hilang” dari array. Simpan tabel pemetaan sebagai konfigurasi terpisah bila standar nama kolom sering berubah.
Meratakan dan Mengelompokkan Struktur #
Dua arah perubahan struktur paling sering dibutuhkan dalam praktik. Meratakan (flatten) mengubah struktur bersarang menjadi satu daftar datar — berguna saat menyiapkan data untuk ekspor. Mengelompokkan (grouping) melakukan sebaliknya: mengumpulkan baris-baris ke dalam keranjang berdasarkan satu kunci, pola yang sama dengan GROUP BY pada database.
<?php
$stokPerKota = [
"Jakarta" => ["kopi", "teh"],
"Bandung" => ["gula", "susu", "roti"],
];
$rata = array_merge(...array_values($stokPerKota));
echo json_encode($rata), "\n";
$pesanan = [
["item" => "kopi", "kategori" => "minuman"],
["item" => "teh", "kategori" => "minuman"],
["item" => "gula", "kategori" => "bahan"],
];
$kelompok = [];
foreach ($pesanan as $p) {
$kelompok[$p["kategori"]][] = $p["item"];
}
echo json_encode($kelompok), "\n";Output yang Dihasilkan:
["kopi","teh","gula","susu","roti"]
{"minuman":["kopi","teh"],"bahan":["gula"]}Baris array_merge(...array_values($stokPerKota)) memakai spread operator ... untuk membongkar setiap array kota menjadi argumen terpisah milik array_merge(), sehingga hasilnya satu daftar datar. Pengelompokan bekerja lewat penulisan $kelompok[$p["kategori"]][]: PHP otomatis membuat array baru saat kategori pertama kali muncul, lalu menambahkan item berikutnya ke array tersebut. Kedua pola ini nantinya muncul lagi ketika kita menyusun laporan penjualan per kota di berbagai chapter pada section ini.
Pengayaan dengan Data Referensi #
Transformasi tidak selalu berdiri sendiri; sering kali satu baris data perlu diperkaya (enrichment) dengan informasi dari tabel referensi — pola yang setara dengan JOIN pada database. Kuncinya: jadikan array referensi berindeks kode unik, sehingga pencarian cukup dengan akses langsung $referensi[$kode]. Cara ini jauh lebih cepat daripada memindai array dengan array_search() berulang kali, dan operator ?? memberi nilai cadangan saat kode tidak dikenal sehingga satu baris aneh tidak menghentikan seluruh proses.
<?php
$infoKategori = [
"K-01" => ["nama" => "Minuman", "pajak" => 0.1],
"K-02" => ["nama" => "Bahan Pokok", "pajak" => 0.0],
];
$penjualan = [
["produk" => "Kopi Arabika", "kode" => "K-01", "qty" => 2, "harga" => 85000],
["produk" => "Gula Aren", "kode" => "K-02", "qty" => 5, "harga" => 32000],
];
$diperkaya = array_map(
function (array $row) use ($infoKategori): array {
$info = $infoKategori[$row["kode"]] ?? ["nama" => "Lainnya", "pajak" => 0.11];
$row["kategori"] = $info["nama"];
$row["pajak"] = (int) round($row["qty"] * $row["harga"] * $info["pajak"]);
return $row;
},
$penjualan
);
foreach ($diperkaya as $row) {
printf("%s — %s — pajak Rp%d\n", $row["produk"], $row["kategori"], $row["pajak"]);
}Output yang Dihasilkan:
Kopi Arabika — Minuman — pajak Rp17000
Gula Aren — Bahan Pokok — pajak Rp0Mari kita bedah contoh di atas:
use ($infoKategori): closure “membawa serta” tabel referensi dari lingkup luar sehingga tabel tidak perlu dibuat ulang di setiap pemanggilan.?? ["nama" => "Lainnya", "pajak" => 0.11]: nilai cadangan saat kode kategori tidak ada — baris tetap diproses, bukan gagal total.(int) round(...): hasil perkalian dengan tarif pajak float dibulatkan lalu dikembalikan sebagai integer agar nilai rupiah selalu utuh.- Baris hasil: setiap baris penjualan kini membawa dua kolom baru,
kategoridanpajak, yang awalnya harus dicari ke tabel referensi.
Memformat Hasil untuk Ditampilkan #
Tahap akhir pipeline adalah menyiapkan tampilan: angka besar butuh pemisah ribuan, tanggal butuh format yang mudah dibaca, dan pertukaran antar sistem butuh JSON. number_format() menerima pemisah desimal dan ribuan kustom, DateTimeImmutable::format() merender tanggal dari objek yang dibuat dengan zona waktu eksplisit, dan json_encode() mengubah array kembali menjadi string dengan flag JSON_UNESCAPED_UNICODE agar karakter non-ASCII tidak berubah menjadi deretan \uXXXX:
<?php
$totalBelanja = 150000;
$tanggal = new DateTimeImmutable("2026-01-15 08:00:00", new DateTimeZone("Asia/Jakarta"));
echo number_format($totalBelanja, 0, ",", "."), "\n";
echo $tanggal->format("d/m/Y H:i"), "\n";
echo json_encode(["status" => "LUNAS", "total" => $totalBelanja]), "\n";Output yang Dihasilkan:
150.000
15/01/2026 08:00
{"status":"LUNAS","total":150000}Pemformatan selalu ditempatkan di ujung pipeline, bukan di tengah. Nilai mentah bertipe angka tetap dibawa apa adanya selama proses berjalan — 150000 tetap integer untuk dihitung — dan format "150.000" hanya dibuat saat nilai benar-benar ditampilkan. Aturan ini mencegah angka terformat tersulap kembali menjadi string yang harus dibersihkan lagi oleh proses lain.
Ringkasan #
Kita kini memiliki pipeline transformasi utuh: normalisasi membersihkan bentuk, koersi memastikan tipe, pengubahan kunci dan struktur menyelaraskan data dari banyak sumber, pengayaan menambah konteks dari tabel referensi, dan pemformatan menyiapkan tampilan akhir. Kombinasi teknik ini akan dipakai berulang kali pada chapter import/export nanti. Namun seluruh contoh sejauh ini berlangsung dalam satu eksekusi dengan data yang muat di memori — ketika jumlah baris membengkak hingga ratusan ribu, kita memerlukan pola pemrosesan bertahap yang dibahas pada Chapter Batch Processing .