Cách phân trang trong PHP và MySQL

0
771

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();

BÌNH LUẬN

Vui lòng nhập bình luận của bạn
Vui lòng nhập tên của bạn ở đây

+ 17 = 19