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 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:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | classPagination{    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     */    functioninit($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($configas$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 😀         */        $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     */    privatefunction__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        returnstr_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é     */    functionhtml()    {           $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:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | $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= newPagination();$paging->init($config);echo$paging->html(); | 
Và đây là tổng hợp code cho bài này:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | <?php classPagination{    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     */    functioninit($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($configas$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 😀         */        $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     */    privatefunction__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        returnstr_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é     */    functionhtml()    {           $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= newPagination();$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> | 
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.
Theo:freetuts.net
 
                                       



 
							 
							 
							 
							 
							 
							 
							 
							 
							 
							 
							 
							 
							 
															
							 
                             
             
            
 Vietnamese
 Vietnamese Afrikaans
 Afrikaans Albanian
 Albanian Amharic
 Amharic Arabic
 Arabic Armenian
 Armenian Azerbaijani
 Azerbaijani Basque
 Basque Belarusian
 Belarusian Bengali
 Bengali Bosnian
 Bosnian Bulgarian
 Bulgarian Catalan
 Catalan Cebuano
 Cebuano Chichewa
 Chichewa Chinese (Simplified)
 Chinese (Simplified) Chinese (Traditional)
 Chinese (Traditional) Corsican
 Corsican Croatian
 Croatian Czech
 Czech Danish
 Danish Dutch
 Dutch English
 English Esperanto
 Esperanto Estonian
 Estonian Filipino
 Filipino Finnish
 Finnish French
 French Frisian
 Frisian Galician
 Galician Haitian Creole
 Haitian Creole Georgian
 Georgian German
 German Greek
 Greek Gujarati
 Gujarati Hausa
 Hausa Hawaiian
 Hawaiian Hebrew
 Hebrew Hindi
 Hindi Hmong
 Hmong Hungarian
 Hungarian Icelandic
 Icelandic Igbo
 Igbo Indonesian
 Indonesian Irish
 Irish Italian
 Italian Japanese
 Japanese Javanese
 Javanese Kannada
 Kannada Kazakh
 Kazakh Khmer
 Khmer Korean
 Korean Kurdish (Kurmanji)
 Kurdish (Kurmanji) Kyrgyz
 Kyrgyz Lao
 Lao Latin
 Latin Latvian
 Latvian Lithuanian
 Lithuanian Luxembourgish
 Luxembourgish Macedonian
 Macedonian Malagasy
 Malagasy Malay
 Malay Malayalam
 Malayalam Maltese
 Maltese Maori
 Maori Marathi
 Marathi Mongolian
 Mongolian Myanmar (Burmese)
 Myanmar (Burmese) Nepali
 Nepali Norwegian
 Norwegian Pashto
 Pashto Persian
 Persian Polish
 Polish Portuguese
 Portuguese Punjabi
 Punjabi Romanian
 Romanian Russian
 Russian Samoan
 Samoan Scottish Gaelic
 Scottish Gaelic Sinhala
 Sinhala Serbian
 Serbian Sesotho
 Sesotho Shona
 Shona Sindhi
 Sindhi Slovenian
 Slovenian Slovak
 Slovak Somali
 Somali Spanish
 Spanish Sundanese
 Sundanese Swahili
 Swahili Swedish
 Swedish Tajik
 Tajik Tamil
 Tamil Telugu
 Telugu Thai
 Thai Turkish
 Turkish Ukrainian
 Ukrainian Urdu
 Urdu Uzbek
 Uzbek Welsh
 Welsh Xhosa
 Xhosa Yiddish
 Yiddish Yoruba
 Yoruba Zulu
 Zulu