PHP TUTORIALS
Hướng dẫn tạo domain ảo tại localhost với XAMPP trên Window Có gì mới trong PHP 8 (Tính năng, Cải tiến và Trình biên dịch JIT) Hướng dẫn viết ứng dụng kiểm tra năm sinh theo âm lịch bằng PHP Xử lý realtime trong PHP sử dụng pusher Hướng dẫn gửi mail trong PHP với PHPMailer Bóc tách dữ liệu từ trang khác bằng PHP Simple HTML DOM Parser Cách sửa lỗi hình ảnh khi đăng bài viết lên Facebook Hướng dẫn tạo thông báo realtime với pushcrew Kích thước chuẩn và cách làm FavIcon icon Tìm hiểu bản chất vòng lặp foreach trong php Những vấn đề nâng cao kỹ năng lập trình trong php Sử Dụng Vòng Lặp Xuất Dữ Liệu Bảng Tính Lương Tìm hiểu thuật toán phân trang trong php Giới hạn số trang trong thuật toán phân trang Bài 01: Đệ quy menu đa cấp với php và mysql - phần 1 Bài 02: Đệ quy menu đa cấp với php và mysql - phần 2 Tích hợp bộ search google vào website Nên dùng count() hay sizeof() để đếm số phần tử của mảng Bài 01: Lấy dữ liệu từ mysql lưu vào file excel với PHPExcel Tạo slug tự động bằng JavaScript và PHP RSS là gì? Cách Tạo RSS cho Website PHP CMS là gì? Các CMS phổ biến hiện nay (update 2024) Web động là gì? Web tĩnh là gì? Chặn referrer từ website simple-share-buttons.com Bảng mã ASCII chuẩn các hệ nhị phân - thập phân - thập lục phân Hướng dẫn tạo Facebook App để lấy App ID và Secret Key Download Facebook SDK cho PHP Mối liên hệ giữa HTML - PHP - MYSQL Tự động post bài viết lên tường với hootsuite.com Kiểm tra người dùng đã đăng nhập hay chưa bằng PHP Lấy video kênh Youtube mới nhất bằng jQuery và Youtube API V3 Đệ quy chuyên mục đa cấp trong PHP toàn tập Font Awesome là gì? Cách sử dụng Font Awesome Tích hợp đăng nhập Google vào Website Thuật toán phân trang với PHP và MySQL Cài đặt LAMP trên Fedora
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Tìm hiểu thuật toán phân trang trong php

Từ khóa phân trang thì có lẽ không xa lạ gì với các bạn nữa đúng không nhỉ? Và cũng rất nhiều bạn gặp khó khăn trong việc xử lý nó. Nên tôi xin phép được mở bài này giới thiệu một lớp phân trang có cách hoạt động giống như freetuts.net vậy.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

1. Giới thiệu thuật toán phân trang

Vì là văn viết nên việc diễn giải thuật toán sẽ gặp khó khăn rất nhiều để người đọc có thể hiểu được, nên trong bài này nếu bạn không hiểu thì có thể tham khảo một số video trên youtube, có rất nhiều video cho bạn lựa chọn.

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. Và trước khi đi vào vấn đề tôi xin đưa ra 3 bài toán như sau:

Bài toán 1: Giả sử trong CSDL của tôi có 1000 sản phẩm, bây giờ tôi muốn chia ra mỗi kho sẽ chứa 50 sản phẩm. Hỏi cần bao nhiêu kho chứa? Câu trả lời cũng đơn giản, 1000/50 = 20 kho chứa. Trong bài toán này số nhà kho cần để chứa hết sản phẩm chính là số trang mà ta tính được, 1000 sản phẩm chính là tổng số record trong CSDL, 50 chính là giới hạn một trang sẽ hiển thị bao nhiêu record.

Bài viết này được đăng tại [free tuts .net]

Bài toán 2. Giả sử CSDL có 1990 sản phẩm,  bây giờ tôi muốn chia mỗi kho chứa 50 sản phẩm. Hỏi bao cần bao nhiêu kho chứa? Ta thấy nếu lấy 1990/50 = 19 (nếu làm tròn). nhưng nếu cần 19 kho thôi thì 40 sản phẩm bị dư đó đặt ở đâu? À đơn giản ta phải lấy thêm 1 kho nữa để chứa nó, vậy ta có tổng cộng 20 kho. Trong bài toán này giải thích cũng giống như bài toán 1, nhưng có một điều khác là trường hợp tổng số record khi chia cho các trang sẽ dư một vài record, như thế ta bắt buộc phải kiếm thêm một kho nữa để chứa.

Bài toán 3: Tiếp tục bài toán 1, giả sử mỗi sản phẩm sẽ được đánh dấu từ 1 -> 1000, và được cất vào kho theo thứ tự tăng dần. Bây giờ tôi muốn các bạn tìm xem ở kho thứ 5 được bắt đầu từ sản phẩm thứ mấy? Và kết thúc ở sản phẩm thứ mấy? để giải bài này ta phải tính toán một chút. Ta thấy mỗi kho sẽ chứa 50 sản phẩm và:

  • Kho thứ nhất bắt đầu từ 1 -> 50
  • Kho thứ hai bắt đầu từ 51 -> 100
  • Kho thứ ba bắt đầu từ 101 -> 150
  • Kho thứ tư bắt đầu từ 151 -> 200
  • Kho thứ năm bắt đầu từ 201 -> 250

Quá đơn giản :D. Bây giờ tôi muốn tìm ở kho thứ 15 bắt đầu từ số nào và kết thúc ở số nào ? Hì hì hơi căng, các bạn rãnh tính đi chứ tôi hơi bận :D. Nhưng bạn có nhận thấy rằng có một quy luật nào đó không? Tôi thì nhận thấy rằng ta có một quy luật để tính như sau:

product_start = ((kho_hien_tai - 1) * so_san_pham_trong_mot_kho) + 1. Không tin bạn tính nhé

  • Tính kho thứ 5: product_start = (5-1)*50 + 1 = 201
  • Tính kho thứ 10: product_start = (10-1)*50 + 1 = 451
  • ...

Với bài toán 3 này liên hệ trong thuật toán phân trang như thế nào? Như bạn biết trong câu lệnh select sẽ có hai tham số là limit và start, limit chính là số record trên một trang và start chính là số thứ tự record bắt đầu, liên hệ với bài toán 3 thì chính là số thứ tự của sản phẩm ở kho thứ x.

Nhưng trong MYSQL thì số record sẽ được tính bắt đầu từ số 0 nên với công thức tính trên ta viết lại như sau:

product_start = ((kho_hien_tai - 1) * so_san_pham_trong_mot_kho) + 1.

Kết luận: Các bước phân trang như sau:

  • Bước 1: Tính tổng số record trong CSDL
  • Bước 2: Xác định số record trên một trang
  • Bước 3: Tính tổng số trang dưa vào tổng số record và số record trên một trang
  • Bước 4: Sau khi có tổng số record thì in ra trình duyệt

2. Thuật toán phân trang đơn giản

Để cho các bạn dễ hình dung tôi sẽ viết một lớp phân trang đơn giản có sử dụng OOP. Các bạn tạo file index.php và điền nội dung này vào:

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
        '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><a href="'.$this->__link('1').'">First</a></li>';
                $p .= '<li><a href="'.$this->__link($this->_config['current_page']-1).'">Prev</a></li>';
            }
            
            // 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><span>'.$i.'</span></li>';
                }
                else{
                    $p .= '<li><a href="'.$this->__link($i).'">'.$i.'</a></li>';
                }
            }

            // Nút last và next
            if ($this->_config['current_page'] < $this->_config['total_page'])
            {
                $p .= '<li><a href="'.$this->__link($this->_config['current_page'] + 1).'">Next</a></li>';
                $p .= '<li><a href="'.$this->__link($this->_config['total_page']).'">Last</a></li>';
            }
            
            $p .= '</ul>';
        }
        return $p;
    }
}
Qua phần comment tôi có giải thích rồi. Và các bạn lưu ý ở 4 bước trên tôi có tính một số đoạn code giúp hệ thống chạy đúng an toàn và bảo mật hơn.

Cách sử dụng:

$config = array(
    'current_page'  => isset($_GET['page']) ? $_GET['page'] : 1, // Trang hiện tại
    'total_record'  => 1900, // Tổng số record
    'limit'         => 10,// limit
    'link_full'     => 'index.php?page={page}',// Link full có dạng như sau: domain/com/page/{page}
    'link_first'    => 'index.php',// Link trang đầu tiên
);

$paging = new Pagination();

$paging->init($config);

echo $paging->html();

Và đây là tổng hợp code cho bài này:

<?php 
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
        '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><a href="'.$this->__link('1').'">First</a></li>';
                $p .= '<li><a href="'.$this->__link($this->_config['current_page']-1).'">Prev</a></li>';
            }
            
            // 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><span>'.$i.'</span></li>';
                }
                else{
                    $p .= '<li><a href="'.$this->__link($i).'">'.$i.'</a></li>';
                }
            }

            // Nút last và next
            if ($this->_config['current_page'] < $this->_config['total_page'])
            {
                $p .= '<li><a href="'.$this->__link($this->_config['current_page'] + 1).'">Next</a></li>';
                $p .= '<li><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'  => 1900, // Tổng số record
    'limit'         => 10,// limit
    'link_full'     => 'index.php?page={page}',// Link full có dạng như sau: domain/com/page/{page}
    'link_first'    => 'index.php',// Link trang đầu tiên
);

$paging = new Pagination();

$paging->init($config);

echo $paging->html();

?>

<style>
    li{float:left; margin: 3px; border: solid 1px gray;}
    a{padding: 5px;}
    span{display:inline-block; padding: 0px 3px; background: blue; color:white }
</style>

thuat toan phan trang don gian gif

3. Lời kết

Ở phần 2 tôi cũng không nói nhiều vì qua những đoạn code tôi đã có comment rõ ràng rồi. Sẽ hơi khó với những bạn chưa rành với thuật toán phân trang này. Ở bài tiếp theo tôi sẽ viết lại thuật toán nâng cao hơn chút xíu, tức là sẽ hiển thị một số trang nhất định thôi chứ không hiển thị tất cả các trang như ở bài này, mời bạn đọc bài giới hạn số trang trong thuật toán phân trang.

Cùng chuyên mục:

Hàm key_exists() trong PHP

Hàm key_exists() trong PHP

Cách sử dụng key_exists() trong PHP

Hàm mysqli_fetch_row() trong PHP

Hàm mysqli_fetch_row() trong PHP

Cách sử dụng mysqli_fetch_row() trong PHP

Hàm end() trong PHP

Hàm end() trong PHP

Cách sử dụng end() trong PHP

Hàm mysqli_field_count() trong PHP

Hàm mysqli_field_count() trong PHP

Cách sử dụng mysqli_field_count() trong PHP

Hàm count() trong PHP

Hàm count() trong PHP

Cách sử dụng count() trong PHP

Hàm mysqli_field_seek() trong PHP

Hàm mysqli_field_seek() trong PHP

Cách sử dụng mysqli_field_seek() trong PHP

Hàm compact() trong PHP

Hàm compact() trong PHP

Cách sử dụng compact() trong PHP

Hàm mysqli_field_tell() trong PHP

Hàm mysqli_field_tell() trong PHP

Cách sử dụng mysqli_field_tell() trong PHP

Hàm array_values() trong PHP

Hàm array_values() trong PHP

Cách sử dụng array_values() trong PHP

Hàm mysqli_free_result() trong PHP

Hàm mysqli_free_result() trong PHP

Cách sử dụng mysqli_free_result() trong PHP

Hàm array_unshift() trong PHP

Hàm array_unshift() trong PHP

Cách sử dụng array_unshift() trong PHP

Hàm mysqli_get_charset() trong PHP

Hàm mysqli_get_charset() trong PHP

Cách sử dụng mysqli_get_charset() trong PHP

Hàm array_shift() trong PHP

Hàm array_shift() trong PHP

Cách sử dụng array_shift() trong PHP

Hàm mysqli_get_client_stats() trong PHP

Hàm mysqli_get_client_stats() trong PHP

Cách sử dụng mysqli_get_client_stats() trong PHP

Hàm array_unique() trong PHP

Hàm array_unique() trong PHP

Cách sử dụng array_unique() trong PHP

Hàm mysqli_get_client_version() trong PHP

Hàm mysqli_get_client_version() trong PHP

Cách sử dụng mysqli_get_client_version() trong PHP

Hàm array_uintesect() trong PHP

Hàm array_uintesect() trong PHP

Cách sử dụng array_uintesect() trong PHP

Hàm mysqli_get_connection_stats() trong PHP

Hàm mysqli_get_connection_stats() trong PHP

Cách sử dụng mysqli_get_connection_stats() trong PHP

Hàm array_sum() trong PHP

Hàm array_sum() trong PHP

Cách sử dụng array_sum() trong PHP

Hàm mysqli_get_host_info() trong PHP

Hàm mysqli_get_host_info() trong PHP

Cách sử dụng mysqli_get_host_info() trong PHP

Top