Thuật toán phân trang thực chất nó cũng giống như việc bạn chia kẹo ở lớp một vậy, giả sử tôi có 100 cái kẹo, bây giờ tôi muốn mỗi bạn nhận 5 cái kẹo, hỏi có bao nhiêu bạn sẽ nhận được? Đơn giản phải không nào, ta lấy 100/5 = 20 bạn.
1) Tính toán chia trang theo số tự nhiên
Ví dụ: CSDL của bạn có 100 sản phẩm, đánh dấu từ 1 – 100 mỗi trang chứa 10 sản phẩm. Bạn thử tìm xem trang thứ 5 sẽ bắt đầu từ sản phấm số mấy và kết thúc ở sản phẩm số mấy?
– Trang 1: từ 1 – 10
– Trang 2: từ 11 – 20
– Trang 3: từ 21 – 30
– Trang 4: từ 31 – 40
– Trang 5: từ 41 – 50
– Trang 6: từ 51 – 60
– Trang 7: từ 61 – 70
– Trang 8: từ 71 – 80
– Trang 9: từ 81 – 90
– Trang 10: từ 91 – 100
Cách tìm trang thứ 5 bắt đầu từ đâu với thuật toán như sau:
product_start = ((trang_hien_tai – 1) * so_san_phan_tren_mot_trang) + 1
Kiểm tra: ((5 – 1) * 10) +1 = 41 // đến đây các bạn đã tìm ra giá trị $start
2) Truy vấn phân trang trong MySQL:
$SQL = “SELECT * FROM SAN_PHAM LIMIT $START, $LIMIT”;
Do MySQL bắt đầu từ 0 nên chúng ta sẽ áp dụng cách tính như bên dưới:
Đi tìm giá trị start và limit trong MySQL
$PAGE = $_GET['PAGE'];
$PER_PAGE = 10; // $LIMIT
$START = ($PAGE > 0) ? (($PAGE - 1) * $PER_PAGE) : $PAGE;
Ví dụ: Trang 1 http://domain.com/sanpham.php?page=1
$start = (1 – 1) * 10 // $start 0
$SQL = “SELECT * FROM SAN_PHAM LIMIT 0,10”;
Ví dụ: Trang 2 http://domain.com/sanpham.php?page=2
$start = (2 – 1) * 10 // $start 10
$SQL = “SELECT * FROM SAN_PHAM LIMIT 10,10”;
3) Xây dựng lớp hiển thị trang trong PHP
<?php
/**
* @package Pagination
*
* User: MAC
* Date: 9/25/2019
* Time: 8:14 AM
*/
class Pagination
{
protected $_config = array(
'current_page' => 1, // Trang hiện tại
'total_record' => 1, // Tổng số record
'total_page' => 1, // Tổng số trang
'limit' => 10,// limit
'start' => 0, // start
'full_page' => false, // Hiện tất cả các page
'page_hiden' => 2, // Trang hiện tại - page_hiden sẽ bị ẩn
'link_full' => '',// Link full có dạng như sau: domain/com/page/{page}
'link_first' => '',// Link trang đầu tiên
);
/*
* Hàm khởi tạo ban đầu để sử dụng phân trang
*/
function init($config = array())
{
/*
* Lặp qua từng phần tử config truyền vào và gán vào config của đối tượng
* trước khi gán vào thì phải kiểm tra thông số config truyền vào có nằm
* trong hệ thống config không, nếu có thì mới gán
*/
foreach ($config as $key => $val){
if (isset($this->_config[$key])){
$this->_config[$key] = $val;
}
}
/*
* Kiểm tra thông số limit truyền vào có nhỏ hơn 0 hay không?
* Nếu nhỏ hơn thì gán cho limit = 0, vì trong mysql không cho limit bé hơn 0
*/
if ($this->_config['limit'] < 0){
$this->_config['limit'] = 0;
}
/*
* Tính total page, công tức tính tổng số trang như sau:
* total_page = ciel(total_record/limit).
* Tại sao lại như vậy? Đây là công thức tính trung bình thôi, ví
* dụ tôi có 1000 record và tôi muốn mỗi trang là 100 record thì
* đương nhiên sẽ lấy 1000/100 = 10 trang đúng không nào :D
*/
$this->_config['total_page'] = ceil($this->_config['total_record'] / $this->_config['limit']);
/*
* Sau khi có tổng số trang ta kiểm tra xem nó có nhỏ hơn 0 hay không
* nếu nhỏ hơn 0 thì gán nó băng 1 ngay. Vì mặc định tổng số trang luôn bằng 1
*/
if (!$this->_config['total_page']){
$this->_config['total_page'] = 1;
}
/*
* Trang hiện tại sẽ rơi vào một trong các trường hợp sau:
* - Nếu người dùng truyền vào số trang nhỏ hơn 1 thì ta sẽ gán nó = 1
* - Nếu trang hiện tại người dùng truyền vào lớn hơn tổng số trang
* thì ta gán nó bằng tổng số trang
* Đây là vấn đề giúp web chạy trơn tru hơn, vì đôi khi người dùng cố ý
* thay đổi tham số trên url nhằm kiểm tra lỗi web của chúng ta
*/
if ($this->_config['current_page'] < 1){
$this->_config['current_page'] = 1;
}
if ($this->_config['current_page'] > $this->_config['total_page']){
$this->_config['current_page'] = $this->_config['total_page'];
}
/*
* Tính start, Như bạn biết trong mysql truy vấn sẽ có limit và start
* Muốn tính start ta phải dựa vào số trang hiện tại và số limit trên mỗi trang
* và áp dụng công tức start = (current_page - 1)*limit
*/
$this->_config['start'] = ($this->_config['current_page'] - 1) * $this->_config['limit'];
}
/*
* Hàm lấy link theo trang
*/
private function __link($page)
{
// Nếu trang < 1 thì ta sẽ lấy link first
if ($page <= 1 && $this->_config['link_first']){
return $this->_config['link_first'];
}
// Ngược lại ta lấy link_full
// Như tôi comment ở trên, link full có dạng domain.com/page/{page}.
// Trong đó {page} là nơi bạn muốn số trang sẽ thay thế vào
return str_replace('{page}', $page, $this->_config['link_full']);
}
/*
* Hàm lấy mã html
* Hàm này ban tạo giống theo giao diện của bạn
* tôi không có config nhiều vì rất rối
* Bạn thay đổi theo giao diện của bạn nhé
*/
function html()
{
$p = '';
// Kiểm tra tổng số trang lớn hơn 1 mới phân trang
if ($this->_config['total_record'] > $this->_config['limit'])
{
$p = '<ul>';
// Nút prev và first
if ($this->_config['current_page'] > 1)
{
$p .= '<li style="display:inline;padding:5px;"><a href="'.$this->__link('1').'">First</a></li>';
$p .= '<li style="display:inline;padding:5px;"><a href="'.$this->__link($this->_config['current_page']-1).'">Prev</a></li>';
}
if (isset($this->_config['full_page']) && $this->_config['full_page'] === false) {
// nếu không thì
$page_hiden = $this->_config['page_hiden'];
$i_from = ($this->_config['current_page'] - $page_hiden) > 0 ? ($this->_config['current_page'] - $page_hiden) : 1;
$i_total = (($this->_config['current_page'] + $page_hiden) > $this->_config['total_page'] ? $this->_config['total_page'] : ($this->_config['current_page'] + $page_hiden));
$p .= ($this->_config['current_page'] - $page_hiden) > 1 ? '<li style="display:inline;padding:5px;">...</li>' : '';
for ($i = $i_from; $i <= $i_total; $i++) {
// Trang hiện tại
if ($this->_config['current_page'] == $i) {
$p .= '<li style="display:inline;padding:5px;"><span>' . $i . '</span></li>';
} else {
$p .= '<li style="display:inline;padding:5px;"><a href="' . $this->__link($i) . '">' . $i . '</a></li>';
}
}
$p .= ($this->_config['current_page'] + $page_hiden) < $this->_config['total_page'] ? '<li style="display:inline;padding:5px;">...</li>' : '';
} else {
// lặp trong khoảng cách giữa min và max để hiển thị các nút
for ($i = 1; $i <= $this->_config['total_page']; $i++) {
// Trang hiện tại
if ($this->_config['current_page'] == $i) {
$p .= '<li style="display:inline;padding:5px;"><span>' . $i . '</span></li>';
} else {
$p .= '<li style="display:inline;padding:5px;"><a href="' . $this->__link($i) . '">' . $i . '</a></li>';
}
}
}
// Nút last và next
if ($this->_config['current_page'] < $this->_config['total_page'])
{
$p .= '<li style="display:inline;padding:5px;"><a href="'.$this->__link($this->_config['current_page'] + 1).'">Next</a></li>';
$p .= '<li style="display:inline;padding:5px;"><a href="'.$this->__link($this->_config['total_page']).'">Last</a></li>';
}
$p .= '</ul>';
}
return $p;
}
}
$config = array(
'current_page' => isset($_GET['page']) ? $_GET['page'] : 1, // Trang hiện tại
'total_record' => 50, // Tổng số record
'limit' => 5,// limit
'full_page' => false,
'page_hiden' => 2,
'link_full' => 'Pagination.php?page={page}',// Link full có dạng như sau: domain/com/page/{page}
'link_first' => 'Pagination.php',// Link trang đầu tiên
);
$paging = new Pagination();
$paging->init($config);
echo $paging->html();