การใช้ PDO กับ Pagination
ในบทเรียนนี้ เราจะมาเรียนรู้วิธีการใช้งาน PDO (PHP Data Objects) เพื่อจัดการกับการแบ่งหน้าข้อมูล หรือ Pagination ซึ่งเป็นฟังก์ชันที่สำคัญมากสำหรับเว็บไซต์ที่มีข้อมูลจำนวนมาก Pagination ช่วยให้ผู้ใช้สามารถนำทางไปยังข้อมูลที่ต้องการได้อย่างง่ายดาย โดยไม่ต้องโหลดข้อมูลทั้งหมดในครั้งเดียว
เราจะครอบคลุมหัวข้อดังนี้:
- การคำนวณจำนวนหน้าทั้งหมด
- การดึงข้อมูลตามหน้าที่ผู้ใช้เลือก
- การสร้างลิงก์สำหรับนำทางไปยังหน้าต่างๆ
การคำนวณจำนวนหน้าทั้งหมด
ขั้นตอนแรกในการทำ Pagination คือการคำนวณจำนวนหน้าทั้งหมดที่เราต้องการแสดงผล จำนวนหน้านี้ขึ้นอยู่กับจำนวนข้อมูลทั้งหมดในฐานข้อมูล และจำนวนข้อมูลที่เราต้องการแสดงผลต่อหนึ่งหน้า
สมมติว่าเรามีตารางชื่อ `products` ที่เก็บข้อมูลสินค้า และเราต้องการแสดงสินค้า 10 รายการต่อหนึ่งหน้า โค้ดต่อไปนี้จะแสดงวิธีการคำนวณจำนวนหน้าทั้งหมด:
<?php
// กำหนดจำนวนรายการต่อหน้า
$itemsPerPage = 10;
// เชื่อมต่อฐานข้อมูล
$dsn = 'mysql:host=localhost;dbname=mydatabase;charset=utf8mb4';
$user = 'username';
$pass = 'password';
try {
$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit;
}
// นับจำนวนรายการทั้งหมดในตาราง
$stmt = $pdo->query("SELECT COUNT(*) FROM products");
$totalItems = $stmt->fetchColumn();
// คำนวณจำนวนหน้าทั้งหมด
$totalPages = ceil($totalItems / $itemsPerPage);
echo "จำนวนหน้าทั้งหมด: " . $totalPages;
?>
คำอธิบายโค้ด:
- `$itemsPerPage = 10;` กำหนดจำนวนรายการที่เราต้องการแสดงผลต่อหนึ่งหน้า
- ส่วนของการเชื่อมต่อฐานข้อมูล (PDO) เหมือนกับที่เราเคยทำในบทเรียนก่อนๆ
- `$stmt = $pdo->query("SELECT COUNT(*) FROM products");` ทำการ Query เพื่อนับจำนวนรายการทั้งหมดในตาราง `products`
- `$totalItems = $stmt->fetchColumn();` ดึงผลลัพธ์จากการ Query (จำนวนรายการทั้งหมด)
- `$totalPages = ceil($totalItems / $itemsPerPage);` คำนวณจำนวนหน้าทั้งหมด โดยใช้ฟังก์ชัน `ceil()` เพื่อปัดเศษขึ้นเสมอ
ข้อผิดพลาดที่พบบ่อย:
- ลืมปัดเศษ: หากไม่ใช้ `ceil()` อาจทำให้จำนวนหน้าไม่ถูกต้อง โดยเฉพาะเมื่อจำนวนรายการทั้งหมดหารด้วยจำนวนรายการต่อหน้าไม่ลงตัว
- การเชื่อมต่อฐานข้อมูลผิดพลาด: ตรวจสอบให้แน่ใจว่าข้อมูลการเชื่อมต่อฐานข้อมูลถูกต้อง
การดึงข้อมูลตามหน้า
เมื่อเราทราบจำนวนหน้าทั้งหมดแล้ว ขั้นตอนต่อไปคือการดึงข้อมูลตามหน้าที่ผู้ใช้เลือก เราจะใช้คำสั่ง `LIMIT` และ `OFFSET` ใน SQL เพื่อกำหนดช่วงของข้อมูลที่เราต้องการดึง
`LIMIT` คือจำนวนรายการที่เราต้องการดึง ในกรณีของเราคือ `$itemsPerPage`
`OFFSET` คือตำแหน่งเริ่มต้นของรายการที่เราต้องการดึง หากเราต้องการดึงข้อมูลในหน้าแรก `OFFSET` จะเป็น 0 หากเป็นหน้าสอง `OFFSET` จะเป็น `$itemsPerPage` และอื่นๆ
โค้ดต่อไปนี้จะแสดงวิธีการดึงข้อมูลตามหน้าที่ผู้ใช้เลือก:
<?php
// กำหนดจำนวนรายการต่อหน้า
$itemsPerPage = 10;
// รับหมายเลขหน้าจาก Query String (เช่น ?page=2)
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// ตรวจสอบว่าหมายเลขหน้าถูกต้อง
if ($page < 1) {
$page = 1;
}
// คำนวณ OFFSET
$offset = ($page - 1) * $itemsPerPage;
// เชื่อมต่อฐานข้อมูล (เหมือนเดิม)
$dsn = 'mysql:host=localhost;dbname=mydatabase;charset=utf8mb4';
$user = 'username';
$pass = 'password';
try {
$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit;
}
// ดึงข้อมูลจากฐานข้อมูล
$stmt = $pdo->prepare("SELECT * FROM products LIMIT :limit OFFSET :offset");
$stmt->bindParam(':limit', $itemsPerPage, PDO::PARAM_INT);
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// แสดงข้อมูล
echo "<ul>";
foreach ($products as $product) {
echo "<li>" . $product['name'] . "</li>";
}
echo "</ul>";
?>
คำอธิบายโค้ด:
- `$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;` รับหมายเลขหน้าจาก Query String หากไม่มี Query String จะกำหนดให้เป็นหน้าแรก (1)
- `if ($page < 1) { $page = 1; }` ตรวจสอบว่าหมายเลขหน้าถูกต้อง หากน้อยกว่า 1 จะกำหนดให้เป็น 1
- `$offset = ($page - 1) * $itemsPerPage;` คำนวณค่า `OFFSET` โดยนำหมายเลขหน้าลบด้วย 1 แล้วคูณด้วยจำนวนรายการต่อหน้า
- `$stmt = $pdo->prepare("SELECT * FROM products LIMIT :limit OFFSET :offset");` เตรียม Prepared Statement โดยใช้ Placeholders `:limit` และ `:offset`
- `$stmt->bindParam(':limit', $itemsPerPage, PDO::PARAM_INT);` และ `$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);` ผูกค่าให้กับ Placeholders โดยระบุชนิดข้อมูลเป็น `PDO::PARAM_INT` เพื่อความปลอดภัย
- `$stmt->execute();` ทำการ Execute Query
- `$products = $stmt->fetchAll(PDO::FETCH_ASSOC);` ดึงข้อมูลทั้งหมดในรูปแบบ Associative Array
- ส่วนของการแสดงผลข้อมูล (loop) เป็นเพียงตัวอย่าง คุณสามารถปรับแต่งให้แสดงข้อมูลในรูปแบบที่ต้องการได้
ข้อผิดพลาดที่พบบ่อย:
- ไม่ได้ตรวจสอบค่า `$_GET['page']`: หากไม่ได้ตรวจสอบค่า `$_GET['page']` อาจทำให้เกิดปัญหาเมื่อผู้ใช้ใส่ค่าที่ไม่ถูกต้อง เช่น ตัวอักษร หรือค่าที่น้อยกว่า 1
- ไม่ได้ใช้ Prepared Statements: การใช้ Prepared Statements ช่วยป้องกัน SQL Injection
- ลืมกำหนดชนิดข้อมูล: การกำหนดชนิดข้อมูล (เช่น `PDO::PARAM_INT`) ช่วยเพิ่มความปลอดภัย
การสร้างลิงก์สำหรับ Pagination
ขั้นตอนสุดท้ายคือการสร้างลิงก์สำหรับนำทางไปยังหน้าต่างๆ เราจะสร้างลิงก์ไปยังหน้าก่อนหน้า, หน้าถัดไป และหน้าอื่นๆ ในช่วงที่กำหนด
โค้ดต่อไปนี้จะแสดงวิธีการสร้างลิงก์สำหรับ Pagination:
<?php
// (ส่วนของการเชื่อมต่อฐานข้อมูลและการคำนวณ $totalPages เหมือนเดิม)
// กำหนดจำนวนรายการต่อหน้า
$itemsPerPage = 10;
// รับหมายเลขหน้าจาก Query String (เช่น ?page=2)
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// ตรวจสอบว่าหมายเลขหน้าถูกต้อง
if ($page < 1) {
$page = 1;
}
// คำนวณ OFFSET
$offset = ($page - 1) * $itemsPerPage;
// เชื่อมต่อฐานข้อมูล (เหมือนเดิม)
$dsn = 'mysql:host=localhost;dbname=mydatabase;charset=utf8mb4';
$user = 'username';
$pass = 'password';
try {
$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
exit;
}
// นับจำนวนรายการทั้งหมดในตาราง
$stmt = $pdo->query("SELECT COUNT(*) FROM products");
$totalItems = $stmt->fetchColumn();
// คำนวณจำนวนหน้าทั้งหมด
$totalPages = ceil($totalItems / $itemsPerPage);
// ดึงข้อมูลจากฐานข้อมูล
$stmt = $pdo->prepare("SELECT * FROM products LIMIT :limit OFFSET :offset");
$stmt->bindParam(':limit', $itemsPerPage, PDO::PARAM_INT);
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
// แสดงข้อมูล
echo "<ul>";
foreach ($products as $product) {
echo "<li>" . $product['name'] . "</li>";
}
echo "</ul>";
// สร้างลิงก์ Pagination
echo "<div class='pagination'>";
// ลิงก์หน้าก่อนหน้า
if ($page > 1) {
echo "<a href='?page=" . ($page - 1) . "'>ก่อนหน้า</a> ";
}
// ลิงก์หน้าต่างๆ
for ($i = 1; $i <= $totalPages; $i++) {
if ($i == $page) {
echo "<span>" . $i . "</span> "; // หน้าปัจจุบัน
} else {
echo "<a href='?page=" . $i . "'>" . $i . "</a> ";
}
}
// ลิงก์หน้าถัดไป
if ($page < $totalPages) {
echo "<a href='?page=" . ($page + 1) . "'>ถัดไป</a>";
}
echo "</div>";
?>
คำอธิบายโค้ด:
- ส่วนของการเชื่อมต่อฐานข้อมูล, การคำนวณ `$totalPages`, และการดึงข้อมูลเหมือนเดิม
- `echo "<div class='pagination'>";` สร้าง `div` เพื่อใส่ลิงก์ Pagination (คุณสามารถใส่ CSS เพื่อตกแต่งได้)
- ส่วนของการสร้างลิงก์:
- `if ($page > 1) { ... }` สร้างลิงก์ "ก่อนหน้า" หากไม่ใช่หน้าแรก
- `for ($i = 1; $i <= $totalPages; $i++) { ... }` สร้างลิงก์ไปยังหน้าต่างๆ โดยวนลูปตั้งแต่หน้า 1 ถึงหน้าสุดท้าย หากเป็นหน้าปัจจุบัน จะแสดงเป็น `span` แทนที่จะเป็นลิงก์
- `if ($page < $totalPages) { ... }` สร้างลิงก์ "ถัดไป" หากไม่ใช่หน้าสุดท้าย
ข้อผิดพลาดที่พบบ่อย:
- ลืมตรวจสอบเงื่อนไข: การลืมตรวจสอบเงื่อนไข (เช่น `$page > 1` หรือ `$page < $totalPages`) อาจทำให้เกิดลิงก์ "ก่อนหน้า" หรือ "ถัดไป" ที่ไม่ถูกต้อง
- ไม่ได้ใส่ CSS: ลิงก์ Pagination ที่ไม่ได้ใส่ CSS อาจดูไม่สวยงาม
ในบทเรียนนี้ เราได้เรียนรู้วิธีการใช้ PDO เพื่อจัดการกับการแบ่งหน้าข้อมูล (Pagination) ซึ่งเป็นฟังก์ชันที่สำคัญมากสำหรับเว็บไซต์ที่มีข้อมูลจำนวนมาก หวังว่าคุณจะสามารถนำความรู้ที่ได้ไปประยุกต์ใช้กับโปรเจกต์ของคุณได้