Baris hasil query dapat dikembalikan dalam beberapa bentuk, dan pemilihan bentuknya menentukan kenyamanan kode pembacanya. Topik ini membahas mode fetch (fetch mode) bawaan dan dua mode array lainnya, serta hidrasi baris ke objek dengan PDO::FETCH_OBJ dan PDO::FETCH_CLASS. Bagian terakhir membandingkan fetchAll() dengan perulangan fetch() baris demi baris. Setelah topik ini dipelajari, bentuk kembalian setiap query dapat dipilih secara sadar sesuai kebutuhan kodenya.
Mode fetch dan bentuk barisnya#
Mode fetch menentukan tipe data PHP yang mewakili satu baris hasil query. Mode bawaan PDO::FETCH_BOTH mengembalikan array yang memuat indeks angka dan kunci nama kolom sekaligus, sehingga setiap nilai tersimpan dua kali dalam satu baris. Mode PDO::FETCH_ASSOC memuat kunci nama kolom saja, sedangkan PDO::FETCH_NUM memuat indeks angka saja. Mode dapat ditetapkan untuk seluruh koneksi melalui opsi PDO::ATTR_DEFAULT_FETCH_MODE, atau untuk satu pernyataan melalui setFetchMode(). Untuk kejelasan kode, PDO::FETCH_ASSOC menjadi pilihan yang paling sering dipakai karena kuncinya menyebut nama kolomnya secara langsung.
Skrip berikut membaca baris yang sama dua kali, pertama dengan mode bawaan dan kedua setelah mode default koneksi diubah menjadi PDO::FETCH_ASSOC.
<?php
declare(strict_types=1);
$pdo = new PDO('sqlite::memory:', null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$pdo->exec('CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT NOT NULL, tahun_terbit INTEGER NOT NULL)');
$pdo->exec("INSERT INTO buku (judul, tahun_terbit) VALUES
('Algoritma Dasar', 2021),
('Jaringan Komputer', 2019),
('Basis Data Relasional', 2022)");
$stmt = $pdo->query('SELECT id, judul, tahun_terbit FROM buku WHERE id = 1');
var_dump($stmt->fetch());
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $pdo->query('SELECT id, judul, tahun_terbit FROM buku WHERE id = 1');
var_dump($stmt->fetch());array(6) {
["id"]=>
int(1)
[0]=>
int(1)
["judul"]=>
string(15) "Algoritma Dasar"
[1]=>
string(15) "Algoritma Dasar"
["tahun_terbit"]=>
int(2021)
[2]=>
int(2021)
}
array(3) {
["id"]=>
int(1)
["judul"]=>
string(15) "Algoritma Dasar"
["tahun_terbit"]=>
int(2021)
}Keluaran pertama memuat enam kunci untuk tiga kolom karena PDO::FETCH_BOTH menduplikasi setiap nilai dengan indeks angka dan kunci nama. Keluaran kedua hanya memuat tiga kunci setelah mode default diubah. Keluaran tersebut juga menunjukkan tipe kolom yang dikembalikan secara alami, yaitu int untuk kolom INTEGER dan string untuk kolom TEXT, sehingga nilai tahun tidak perlu dikonversi lagi di sisi PHP.
Hidrasi baris ke objek#
Baris juga dapat dihidrasi menjadi objek, yaitu proses mengisi nilai kolom ke properti objek yang baru dibuat. Mode PDO::FETCH_OBJ membuat objek stdClass yang propertinya dinamai menurut nama kolom. Mode PDO::FETCH_CLASS berjalan satu langkah lebih jauh karena PDO membuat objek dari kelas pilihan dan mengisi propertinya dari kolom yang bernama sama. Dengan demikian, metode kelas tersebut dapat dipanggil pada baris yang dihidrasi. Syaratnya, nama kolom dan nama properti harus sama persis, sedangkan kolom tanpa properti padanan diberikan sebagai properti dinamis yang sejak PHP 8.2 memicu peringatan.
Skrip berikut menghidrasi baris yang sama dengan kedua mode tersebut. Kelas Buku menyediakan metode ringkas() yang menyusun teks dari dua propertinya.
<?php
declare(strict_types=1);
class Buku
{
public int $id = 0;
public string $judul = '';
public int $tahun_terbit = 0;
public function ringkas(): string
{
return "{$this->judul} ({$this->tahun_terbit})";
}
}
$pdo = new PDO('sqlite::memory:', null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$pdo->exec('CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT NOT NULL, tahun_terbit INTEGER NOT NULL)');
$pdo->exec("INSERT INTO buku (judul, tahun_terbit) VALUES
('Algoritma Dasar', 2021),
('Jaringan Komputer', 2019)");
$stmt = $pdo->query('SELECT id, judul, tahun_terbit FROM buku WHERE id = 1');
$baris = $stmt->fetch(PDO::FETCH_OBJ);
echo get_class($baris), ': ', $baris->judul, "\n";
$stmt = $pdo->query('SELECT id, judul, tahun_terbit FROM buku ORDER BY id');
$semua = $stmt->fetchAll(PDO::FETCH_CLASS, Buku::class);
foreach ($semua as $buku) {
echo $buku->ringkas(), "\n";
}
echo get_class($semua[0]), "\n";stdClass: Algoritma Dasar
Algoritma Dasar (2021)
Jaringan Komputer (2019)
BukuBaris pertama keluaran menegaskan bahwa PDO::FETCH_OBJ menghasilkan objek stdClass, sedangkan tiga baris berikutnya menunjukkan objek Buku yang propertinya berisi nilai kolom sehingga metodenya dapat dipanggil. Pemanggilan fetchAll() dengan argumen kelas menghidrasi seluruh baris sekaligus ke dalam array objek Buku, dan pemeriksaan get_class() pada elemen pertamanya menegaskan kelasnya.
fetchAll dan perulangan baris demi baris#
Metode fetchAll() memuat seluruh baris hasil ke dalam satu array, sehingga jumlah barisnya langsung terbaca dengan count() dan isinya dapat diakses bebas berulang kali. Sebaliknya, fetch() mengembalikan satu baris saja pada setiap pemanggilan dan false setelah baris habis, sehingga pola perulangan while memproses satu baris pada satu waktu. Kedua pola membaca pernyataan yang sama, tetapi kebutuhan memorinya berbeda, karena fetchAll() menahan seluruh hasil di memori sedangkan perulangan hanya menahan baris yang sedang diproses.
Skrip berikut menampilkan tabel yang sama dengan kedua pola tersebut.
<?php
declare(strict_types=1);
$pdo = new PDO('sqlite::memory:', null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$pdo->exec('CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT NOT NULL, penulis TEXT NOT NULL)');
$pdo->exec("INSERT INTO buku (judul, penulis) VALUES
('Algoritma Dasar', 'Citra'),
('Jaringan Komputer', 'Budi'),
('Basis Data Relasional', 'Dewi')");
$semua = $pdo->query('SELECT judul, penulis FROM buku')->fetchAll();
echo 'fetchAll: ', count($semua), ' baris', "\n";
$stmt = $pdo->query('SELECT judul, penulis FROM buku');
while ($baris = $stmt->fetch()) {
echo $baris['judul'], ' oleh ', $baris['penulis'], "\n";
}fetchAll: 3 baris
Algoritma Dasar oleh Citra
Jaringan Komputer oleh Budi
Basis Data Relasional oleh DewiBaris pertama keluaran berasal dari fetchAll() yang jumlah barisnya dihitung langsung dari arraynya, sedangkan tiga baris berikutnya dicetak dari perulangan yang berhenti ketika fetch() mengembalikan false. Untuk pernyataan SELECT, hasil rowCount() tidak dijamin konsisten antar driver, sehingga jumlah baris dibaca dari array hasil fetchAll(). Sebaliknya, rowCount() dapat diandalkan untuk INSERT, UPDATE, dan DELETE karena mengembalikan jumlah baris yang terpengaruh.
Kesalahan umum#
- Nama kolom dan nama properti tidak sama pada
PDO::FETCH_CLASS. Kolomtahun_terbittidak akan mengisi properti$tahunTerbit, sehingga nilainya muncul sebagai properti dinamis, sedangkan properti bertipe$tahunTerbittetap memegang nilai awalnya. Samakan namanya, atau ganti nama kolom pada query denganSELECT tahun_terbit AS tahunTerbit. - Memanggil
fetch()setelahfetchAll(). Kursor pernyataan, yaitu penanda posisi pembacaan baris, sudah berada di ujung hasil setelah seluruh baris dimuat, sehingga panggilan berikutnya mengembalikanfalsetanpa pesan apa pun. Ambil data satu kali, lalu baca kembali dari variabel yang menampungnya. - Membiarkan mode
BOTHuntuk hasil yang besar. Setiap baris tersimpan dua kali di memori tanpa keuntungan apa pun bila indeks angkanya tidak dipakai. TetapkanPDO::FETCH_ASSOCsebagai mode default koneksi sejak awal. - Menganggap satu panggilan
fetch()memuat seluruh hasil. Metodefetch()mengembalikan satu baris saja, sehingga kode yang mengharapkan array berisi semua baris kehilangan data tanpa pesan error. GunakanfetchAll()bila seluruh baris memang dibutuhkan sekaligus, atau bungkusfetch()dalam perulangan yang berhenti padafalse.
Ringkasan#
- Mode fetch menentukan bentuk satu baris hasil, yaitu array campuran pada
PDO::FETCH_BOTH, array asosiatif padaPDO::FETCH_ASSOC, atau array indeks angka padaPDO::FETCH_NUM. PDO::FETCH_OBJmenghidrasi baris kestdClass, sedangkanPDO::FETCH_CLASSmenghidrasinya ke kelas pilihan dengan syarat nama kolom dan properti sama.fetchAll()memuat seluruh baris sekaligus dan mudah dihitung, sedangkan perulanganfetch()memproses satu baris pada satu waktu dan lebih hemat memori.rowCount()diandalkan untukINSERT,UPDATE, danDELETE, bukan untuk menghitung baris hasilSELECT.
Referensi#
- PDOStatement::fetch — Manual PHP — Mendokumentasikan mode fetch dan bentuk kembaliannya.
- PDOStatement::fetchAll — Manual PHP — Menjelaskan pengambilan seluruh baris sekaligus beserta argumennya.
- PDOStatement::setFetchMode — Manual PHP — Menetapkan mode fetch untuk satu pernyataan.