Xây dựng class phân trang OPP

Trong bài viết trước mình đã giới thiệu tới các bạn cách xử lý phân trang cũng khá là hoàn chỉnh. Nhưng như vậy thì có lẽ vẫn là chưa đủ chẳng nhẽ cứ chỗ nào cần phân trang thì chúng ta lại phải viết lại những mẩu code đó. Thì trong bài viết này mình sẽ hướng dẫn các bạn tách chúng ra và gộp nó lại ...

Trong bài viết trước mình đã giới thiệu tới các bạn cách xử lý phân trang cũng khá là hoàn chỉnh. Nhưng như vậy thì có lẽ vẫn là chưa đủ chẳng nhẽ cứ chỗ nào cần phân trang thì chúng ta lại phải viết lại những mẩu code đó. Thì trong bài viết này mình sẽ hướng dẫn các bạn tách chúng ra và gộp nó lại thành một đối tượng chuyên xử lý phân trang trong ứng dụng của bạn.

Lưu ý quan trọng:

Để xây dựng được một class phân trang hoàn chỉnh thì nó chỉ phụ thuộc vào tối thiểu bốn yếu tố đầu vào như sau:

  • $total_records: Tổng số records của bạn.
  • $limit: Số records quy định trên một trang.
  • $current_page :Trang hiện tại bạn đang đứng
  • $num_pages: Số liên kết trang sẽ hiển thị trên một trang

Như vậy bạn chỉ cần có đầy đủ ít nhất bốn tham số đầu vào này là bạn có thể xây dựng một class phân trang hoàn chỉnh. Có nhiều bạn khi xây dựng một class xử lý phân trang có truyền cả câu truy vấn vào trong class theo mình thì điều đó là không nên. Hoặc có bạn hỏi mình rằng hướng dẫn xây dựng một class phân trang bằng PDO. Bằng gì không quan trọng bạn chỉ cần bạn cung cấp ít nhất bốn tham số kia là được rồi.

1. Main class

class Pagination{
	public $current_page	= 1;	// Trang hiện tại
	public $num_pages		= 5;	// Số liên kết trang sẽ hiển thị
	public $total_pages 	= 0;	// Tổng số trang
	public $total_records	= 0;	// Tổng số records
	public $mid_range		= 2;	
	public $limit			= 10;	// Số records sẽ hiển thị
	public $limit_start		= 0;	// Bắt đầu từ trang
	public $limit_end		= 0;	// Kết thúc tới trang
	public $link_first		= ';	// Link đầu tiên
	public $link_full		= ';	// Link đầy đủ
	public $prefix			= 'page';	// get var
	public $hashtag			= ';	// tag
	public $pagination_type	= 1;	// Kiểu phân trang
	public $class 			= array(
		'active'	=> 'current'
	);
	var $button				= array(
		'first'	=>	'First',
		'prev'	=>	'Prev',
		'next'	=>	'Next',
		'last'	=>	'Last',
	);
	/**
	* Constructor
	*/
	public function __construct($config = array()){
		if(is_array($config)){
			foreach($config as $k => $v){
				$this->{$k} = $v;
			}
		}
		// Kiểm tra currennt_page
		if($this->current_page < 0){
			$this->current_page = 1;
		}
		// Kiểm tra number_per_page
		if($this->limit <= 0){
			$this->limit = 10;
		}
		// Tính mid_range
		$this->mid_range = floor($this->num_pages / 2);
		// Tính total_page
		$this->total_pages = ceil($this->total_records / $this->limit);
		
		// Kiểm tra current_page
		if ($this->current_page > $this->total_pages){
			$this->current_page = $this->total_pages;
		}
		// Tính {$limit_start} và {$limit_end}
		if($this->current_page >= $this->num_pages){
			$this->limit_start = $this->current_page - $this->mid_range;
			if($this->total_pages > ($this->current_page + $this->mid_range)){
				$this->limit_end = $this->current_page + $this->mid_range;
			}
			else if($this->current_page <= $this->total_pages 
			&& $this->current_page > ($this->total_pages - ($this->mid_range - 1))){
				$this->limit_start = $this->total_pages - ($this->mid_range - 1);
				$this->limit_end = $this->total_pages;
			}else{
				$this->limit_end = $this->total_pages;
			}
		}else{
			$this->limit_start = 1;
			$this->limit_end = ($this->total_pages > $this->num_pages) 
				? $this->num_pages 
				: $this->total_pages;
		}
	}
	function _makequery() {
		$qs = ';
		if (!empty($_SERVER['QUERY_STRING'])) {
			$parts = explode("&", $_SERVER['QUERY_STRING']);
			$query_array = array();
			foreach ($parts as $val) {
				if (stristr($val, $this->prefix) == false)  {
					array_push($query_array, $val);
				}
			}
			if (count($query_array) != 0) {
				$qs = "&".implode("&", $query_array);
			}  
		}
		if ($this->hashtag != ') $qs .= $this->hashtag;
		return $qs; 
	}
	/* Hàm make link */
	function _makelink($page){
		if(!$this->pagination_type){
			if($page <= 1){
				return $_SERVER['PHP_SELF'].$this->_makequery();
			}else{
				return $_SERVER['PHP_SELF']."?{$this->prefix}={$page}".$this->_makequery();
			}
		}else{
			if(substr($this->link_full,0,-1)=='/'){
				$this->link_full = substr($this->link_full,0,-1);
			}
			if($page <= 1 && !empty($this->link_first)){
				return $this->link_first;
			}else{
				return $this->link_full."{$this->prefix}-{$page}.html";
			}
		}
	}
	/* Hàm render pagination */
	function render(){
		$p = ';
		if($this->total_pages > 1){
			$p .= '<ul class="pagination">';
			// Button first và prev
			if($this->current_page > 1){
				$p .= '<li><a rel="nofollow" href="'.$this->_makelink('1').'">'.$this->button['first'].'</a></li>';
				$p .= '<li><a rel="nofollow" href="'.$this->_makelink($this->current_page-1).'">'.$this->button['prev'].'</a></li>';
			}
			 // lặp trong khoảng cách giữa {$limit_start} và {$limit_end} để hiển thị các nút
			for($i = $this->limit_start; $i <= $this->limit_end; $i++){
				// Trang hiện tại
				if($this->current_page==$i){
					$p .= '<li><span class="'.$this->class['active'].'">'.$i.'</span></li>';
				}else{
					$p .= '<li><a rel="nofollow" href="'.$this->_makelink($i).'">'.$i.'</a></li>';
				}
			}
			// Button last và next
			if($this->current_page < $this->total_pages){
				$p .= '<li><a rel="nofollow" href="'.$this->_makelink($this->current_page+1).'">'.$this->button['next'].'</a></li>';
				$p .= '<li><a rel="nofollow" href="'.$this->_makelink($this->total_pages).'">'.$this->button['last'].'</a></li>';
			}
		}
		return $p;
	}
}

Lưu ý:

Trong class này mình chia làm hai kiểu phân trang là biến $pagination_type

  • Kiểu 1: Dạng liên kết http://example.com/?page=[...][QUERY_STRING]
  • Kiểu 2: Dạng liên kết http://example.com/[...]/trang-[...].html

2. Cách sử dụng

Tiếp theo ví dụ trước chúng ta chỉ cần tóm tắt lại bằng một số dòng lệnh sau.

require_once('/path_to_class_folder/clsPagination.php');
$config = array(
	'total_records'	=> $total_records,
	'current_page'	=> $current_page,
	'limit'	=> $limit,
	'num_pages'	=> $num_pages,
	'pagination_type'	=> false
);
$clsPagination = new Pagination($config);
$pagination_view = $clsPagination->render();

// Xuất ra danh sách phân trang
echo $pagination_view;

3. Code hoàn chỉnh cho cả ví dụ.

<?php
    define("DB_HOST", "localhost");
    /** MYSQL databse name */
    define("DB_NAME", "demo_db");
    /** MySQL database username */
    define("DB_USER", "root");
    /** MySQL database password */
    define("DB_PASS", "");
     
    // Bước 1: Kết nối tới CSDL
    $dbconn = mysql_connect(DB_HOST,DB_USER,DB_PASS) or die('Không thể kết nối tới CSDL');
    mysql_select_db(DB_NAME, $dbconn) or die('Không thể kết nối tới CSDL!');
     
    // Bước 2: Tính tổng $totalRecords
    $query = "SELECT count(*) AS total_records FROM employees";
    $result = mysql_query($query, $dbconn);
    $row = mysql_fetch_assoc($result);
    $total_records = !empty($row) ? $row['total_records'] : 0;
     
    // Bước 3: Lấy $currentPage và thiết lập $recordPerPage
    $current_page = isset($_GET['page']) && (int) $_GET['page'] > 0 ? (int) $_GET['page'] : 1;
    // Thiết lập số records/1 trang
    $limit = 3;
    // Thiết lập số trang
    $num_pages = 5;
    
	require_once('/path_to_class_folder/clsPagination.php');
	$config = array(
		'total_records'	=> $total_records,
		'current_page'	=> $current_page,
		'limit'	=> $limit,
		'num_pages'	=> $num_pages,
		'pagination_type'	=> false
	);
	$clsPagination = new Pagination($config);
	$pagination_view = $clsPagination->render();
     
    // Tính $offset
    $offset = ($current_page-1)*$limit;
    $limit = "LIMIT $offset,$limit";
     
    $query = "SELECT * FROM employees ".$limit; 
    $result = mysql_query($query, $dbconn);
    // PHẦN HIỂN THỊ DANH SÁCH
?>
<div class="container">
    <?php if(mysql_num_rows($result) > 0){ ?>
        <table class="tbl-grid" cellpadding="0" cellspacing="0" awidth="100%">
            <thead><tr>
                <td class="gridheader">employeeNumber</td>
                <td class="gridheader">lastName</td>
                <td class="gridheader">firstName</td>
                <td class="gridheader">extension</td>
                <td class="gridheader">email</td>
                <td class="gridheader">officeCode</td>
                <td class="gridheader">reportsTo</td>
                <td class="gridheader">jobTitle</td>
            </tr></thead>
        <?php while ($row = mysql_fetch_assoc($result)){?>
            <tr>
                <td><?php echo $row['employeeNumber'] ?></td>
                <td><?php echo $row['lastName'] ?></td>
                <td><?php echo $row['firstName'] ?></td>
                <td><?php echo $row['extension'] ?></td>
                <td><?php echo $row['email'] ?></td>
                <td><?php echo $row['officeCode'] ?></td>
                <td><?php echo $row['reportsTo'] ?></td>
                <td><?php echo $row['jobTitle'] ?></td>
            </tr>
        <?php } ?>
        </table>
    <?php } ?> 
    <br />
	<?php echo $pagination_view; ?>
</div>

Note: Có một bạn từng hỏi mình rằng làm sao có thể tạo được 2 vùng phân trang cùng trên một page. Thì trong bài viết này mình xin trả lời điều đó rất đơn giản và bạn không cần xử dụng ajax hoặc một cái gì đó quá khó khăn.

Trong class của mình có một biến là $prefix. Đó chính là điểm mấu chốt của vấn đề và bạn có thể tùy chỉnh thông số này thay vì bạn fix tĩnh nó sẽ mất đi tính mềm dẻo của class.

Trường hợp 1: Không sử dụng htaccess.

Phân trang 1: bạn đặt prefix là page_1.

$current_page = $_GET['page_1'];

Phân trang 2: bạn đặt prefix là page_2.

$current_page = $_GET['page_2'];

Trường hợp 2: Có sử dụng htaccess để làm đẹp link.

Bạn có hai link như sau để trỏ về cùng một.

http://example.com/abc/page_1-1.html

http://example.com/abc/page_2-1.html

RewriteRule ^abc/page_1-([0-9]*).html index.php?module=abc?page_1=$1
RewriteRule ^abc/page_2-([0-9]*).html index.php?module=abc?page_2=$1

Hoặc bạn có thể coi prefix cũng là một biến.

RewriteRule ^abc/(.*)-([0-9]*).html index.php?modeule=abc&prefix=$1&page=$2

Khi đó.

  • $prefix = page_1 là của phân trang thứ nhất.
  • $prefix = page_2 là của phân trang thứ hai.

4.Tổng kết.

Tổng kết

    0