Trong phát triển ứng dụng web bằng PHP, việc xử lý dữ liệu lớn từ cơ sở dữ liệu MySQL là điều không thể tránh khỏi. Nếu bạn đã từng làm việc với kết nối MySQL bằng PDO hoặc triển khai chức năng CRUD trong PHP thuần, chắc chắn bạn sẽ gặp trường hợp bảng dữ liệu có hàng nghìn bản ghi. Nếu hiển thị toàn bộ dữ liệu này trên một trang duy nhất, hệ thống sẽ vừa nặng nề vừa gây khó khăn cho người dùng trong việc tìm kiếm và thao tác.
Chính vì vậy, phân trang (pagination) trở thành giải pháp không thể thiếu. Nhờ kỹ thuật phân trang, chúng ta có thể hiển thị dữ liệu một cách gọn gàng, dễ đọc và thân thiện với trải nghiệm người dùng. Hơn nữa, khi kết hợp với các kỹ thuật đã từng đề cập trong các bài như giữ đăng nhập bằng Session hoặc xử lý upload file ảnh trong PHP, bạn sẽ xây dựng được một hệ thống web hoàn chỉnh, có khả năng mở rộng và quản lý hiệu quả.
Khái niệm phân trang trong PHP
Trước khi đi sâu vào cài đặt, bạn cần hiểu phân trang là gì. Về cơ bản, phân trang nghĩa là chia nhỏ dữ liệu thành nhiều trang khác nhau. Ví dụ, bạn có 500 bản ghi trong bảng users, thay vì hiển thị hết 500 dòng, bạn có thể chia thành 50 trang, mỗi trang hiển thị 10 dòng. Cách làm này không chỉ giúp tối ưu hiệu suất hệ thống mà còn mang đến trải nghiệm trực quan hơn cho người dùng.
Trong MySQL, hai công cụ quan trọng để thực hiện phân trang chính là LIMIT và OFFSET. LIMIT được dùng để giới hạn số bản ghi hiển thị, còn OFFSET quy định bắt đầu lấy dữ liệu từ đâu. Khi kết hợp hai tham số này, bạn có thể dễ dàng lấy dữ liệu cho từng trang cụ thể.
Ví dụ, để lấy 10 bản ghi đầu tiên, ta dùng:
SELECT * FROM users LIMIT 10 OFFSET 0;
Với câu lệnh trên, hệ thống sẽ chỉ trả về 10 bản ghi đầu tiên. Nếu muốn hiển thị từ bản ghi thứ 11 đến 20, ta chỉ cần thay đổi OFFSET thành 10. Đây chính là nền tảng để chúng ta xây dựng chức năng phân trang trong PHP.
Kết nối MySQL bằng PDO trước khi phân trang
Để triển khai phân trang, bước đầu tiên là kết nối đến cơ sở dữ liệu. Trong PHP, có hai lựa chọn phổ biến là MySQLi và PDO. Tuy nhiên, PDO thường được đánh giá cao hơn bởi sự bảo mật, tính linh hoạt và khả năng hỗ trợ nhiều hệ quản trị cơ sở dữ liệu khác nhau. Điều này đã được phân tích kỹ trong bài So sánh MySQLi và PDO.
Việc sử dụng PDO không chỉ giúp bạn viết code gọn gàng hơn mà còn giảm thiểu nguy cơ bị tấn công SQL Injection nhờ hỗ trợ prepared statement. Đây là lý do hầu hết các dự án hiện đại đều lựa chọn PDO. Trước khi viết logic phân trang, ta cần một đoạn code để kết nối cơ sở dữ liệu bằng PDO.
<?php
$host = "localhost";
$dbname = "mydb";
$username = "root";
$password = "";
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Kết nối thất bại: " . $e->getMessage());
}
?>
Trong đoạn code trên, chúng ta khai báo thông tin cơ bản như host, tên database, username và password. Sau đó, PDO được khởi tạo trong khối try...catch để xử lý ngoại lệ. Nếu kết nối thất bại, chương trình sẽ dừng và báo lỗi. Cách làm này an toàn và phù hợp cho mọi dự án web thực tế.
Lấy tổng số bản ghi trong bảng
Để phân trang chính xác, bạn cần biết tổng số bản ghi hiện có trong bảng dữ liệu. Nếu không có con số này, bạn sẽ không thể tính toán số lượng trang cần hiển thị. Ví dụ, nếu bảng có 100 bản ghi và bạn hiển thị 10 bản ghi trên mỗi trang, bạn sẽ cần 10 trang. Nếu có 105 bản ghi, thì số trang cần thiết sẽ là 11.
Kỹ thuật lấy tổng số bản ghi rất đơn giản. Ta chỉ cần sử dụng câu lệnh SQL COUNT(*). Sau đó, ta dùng PDO để thực thi truy vấn và trả về kết quả.
<?php
$stmt = $pdo->query("SELECT COUNT(*) FROM users");
$total_records = $stmt->fetchColumn();
?>
Đoạn code này trả về tổng số bản ghi trong bảng users. Biến $total_records sẽ được dùng để tính toán tổng số trang. Đây là bước quan trọng giúp hệ thống phân trang hoạt động chính xác, không bị dư hoặc thiếu dữ liệu.
Tính toán LIMIT và OFFSET
Sau khi biết tổng số bản ghi, ta cần xác định số bản ghi hiển thị trên mỗi trang (LIMIT) và trang hiện tại mà người dùng đang truy cập. OFFSET được tính dựa trên số trang. Ví dụ, nếu người dùng ở trang 1, OFFSET sẽ là 0. Nếu ở trang 2, OFFSET bằng số bản ghi mỗi trang.
Việc tính toán này thường dựa vào tham số page truyền qua URL. Đây là cách phổ biến nhất trong PHP để xác định trang hiện tại.
$limit = 10; // số bản ghi trên mỗi trang $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; $offset = ($page - 1) * $limit;
Trong đoạn code trên, nếu người dùng không truyền tham số page, hệ thống sẽ mặc định hiển thị trang 1. Công thức tính OFFSET là (trang hiện tại - 1) * số bản ghi trên mỗi trang. Điều này đảm bảo dữ liệu luôn được lấy chính xác theo từng trang.
Truy vấn dữ liệu với LIMIT và OFFSET
Sau khi có $limit và $offset, ta sẽ dùng chúng để truy vấn dữ liệu. Việc này được thực hiện bằng prepared statement của PDO để đảm bảo an toàn. Đây là một bước quan trọng vì nếu dùng chuỗi nối thẳng vào câu lệnh SQL, hệ thống sẽ dễ bị tấn công SQL Injection.
<?php
$sql = "SELECT * FROM users LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
Trong code trên, chúng ta bind giá trị LIMIT và OFFSET vào câu lệnh SQL. Điều này giúp PDO hiểu rằng đây là số nguyên, không phải chuỗi lạ có thể gây ra lỗi bảo mật. Kết quả trả về sẽ là mảng $users chứa dữ liệu của từng người dùng.
Hiển thị dữ liệu trong bảng HTML
Sau khi có dữ liệu, công việc tiếp theo là hiển thị ra giao diện người dùng. Thông thường, dữ liệu được hiển thị dưới dạng bảng HTML. Người dùng có thể dễ dàng đọc, sắp xếp hoặc thao tác với từng dòng dữ liệu.
<table border="1">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
</tr>
<?php foreach ($users as $user): ?>
<tr>
<td><?= $user['id'] ?></td>
<td><?= $user['username'] ?></td>
<td><?= $user['email'] ?></td>
</tr>
<?php endforeach; ?>
</table>
Đoạn code này tạo một bảng có ba cột: ID, Username và Email. Với vòng lặp foreach, ta lần lượt hiển thị từng bản ghi. Đây chính là bước cuối cùng để người dùng có thể thấy dữ liệu đã được phân trang.
Kết luận
Qua bài viết này, bạn đã hiểu cách tạo phân trang với LIMIT và OFFSET trong PHP. Đây là kỹ thuật cơ bản nhưng rất quan trọng khi làm việc với cơ sở dữ liệu lớn. Phân trang không chỉ giúp tối ưu hiệu suất mà còn cải thiện trải nghiệm người dùng.
Bạn có thể kết hợp phân trang với các tính năng khác như CRUD trong PHP, validate form, upload file hoặc Session để xây dựng hệ thống hoàn chỉnh và mạnh mẽ.

