Phân trang một mảng trong PHP

Như thế nào được gọi là phân trang một mảng dữ liệu . Mình giả sử bạn có một cái mảng dữ liệu có khoảng vài chục tới vài trăm phần tử. Nếu mà bạn hiển thị tất cả các phần tử này nên một webpage thì bạn nhận thấy rằng nó quá dài. Và bạn muốn phân chúng thành nhiều trang. Thông thường thì có lẽ ...

Như thế nào được gọi là phân trang một mảng dữ liệu. Mình giả sử bạn có một cái mảng dữ liệu có khoảng vài chục tới vài trăm phần tử. Nếu mà bạn hiển thị tất cả các phần tử này nên một webpage thì bạn nhận thấy rằng nó quá dài. Và bạn muốn phân chúng thành nhiều trang.

Thông thường thì có lẽ bạn đã quen thuộc với việc xây dựng một thuật toán phân trang truyền thống có nghĩa là bạn sẽ lấy dữ liệu trực tiếp trong CSDL và sử dụng mệnh đề LIMIT để giới hạn số records bạn muốn trả về. Thì các bài viết về phân trang này mình sẽ không nhắc lại nữa vì trên website này mình cũng có một số bài viết khá chi tiết rồi bạn có thể tham khảo.

Thuật toán phân trang với PHP và MySQL

[Nâng cao] Phân trang trong PHP và MySQL

Mình sẽ lấy hai trong một số lý do tại sao bạn không áp dụng được cách phân trang truyền thống.

  • Bạn cần đọc tất cả các file hình ảnh từ một thư mục.
  • Kết quả là sự kết hợp của câu lệnh SELECT từ nhiều bảng khác nhau.

Để giải quyết vấn đề trên thì hôm nay mình sẽ hướng dẫn các bạn phân trang một mảng dữ liệu.

Project
|-- pagination.class.php
|-- index.php

Thì việc đầu tiên bạn cần xây dựng pagination.class.php phân trang mảng.

<?php

  class paginationArray{

    /**
     * Properties array
     * @var array   
     * @access private 
     */
    private $_properties = array();

	/**
	* Default configurations
	* @var array  
	* @access public 
	*/
	public $_defaults = array(
		'page' => 1,
		'record_per_page' => 10,
		'param' 
	);

	/**
	* Constructor
	* 
	* @param array $array   Array of results to be paginated
	* @param int   $current_page The current page interger that should used
	* @param int   $record_per_page The amount of items that should be show per page
	* @return void    
	* @access public  
	*/
	public function __construct($array, $current_page = null, $record_per_page = null, $params=null){
		$this->array   = $array;
		$this->current_page = ($current_page == null ? $this->defaults['page'] : $current_page);
		$this->record_per_page = ($record_per_page == null ? $this->defaults['record_per_page'] : $record_per_page);
		$this->params	 = $params; 
	}

	/**
	* Global setter
	* 
	* Utilises the properties array
	* 
	* @param string $name  The name of the property to set
	* @param string $value The value that the property is assigned
	* @return void    
	* @access public  
	*/
	public function __set($name, $value) { 
		$this->_properties[$name] = $value;
	} 

	/**
	* Global getter
	* 
	* Takes a param from the properties array if it exists
	* 
	* @param string $name The name of the property to get
	* @return mixed Either the property from the internal
	* properties array or false if isn't set
	* @access public  
	*/
    public function __get($name) {
		if (array_key_exists($name, $this->_properties)) {
			return $this->_properties[$name];
		}
		return false;
    }

	/**
	* Set the show first and last configuration
	* 
	* This will enable the "<< first" and "last >>" style
	* links
	* 
	* @param boolean $showFirstAndLast True to show, false to hide.
	* @return void    
	* @access public  
	*/
    public function setShowFirstAndLast($showFirstAndLast) {
      $this->_showFirstAndLast = $showFirstAndLast;
    }

    /**
     * Set the main seperator character
     * 
     * By default this will implode an empty string
     * 
     * @param string $mainSeperator The seperator between the page numbers
     * @return void    
     * @access public  
     */
    public function setMainSeperator($mainSeperator) {
      $this->mainSeperator = $mainSeperator;
    }

	/**
	* Get the result portion from the provided array 
	* 
	* @return array Reduced array with correct calculated offset 
	* @access public 
	*/
	public function getResults(){
		// Assign the page variable
		if (empty($this->current_page) !== false) {
			$this->page = $this->current_page; // using the get method
		} else {
			$this->page = 1; // if we don't have a page number then assume we are on the first page
		}
		
		// Take the length of the array
		$this->length = count($this->array);
		
		// Get the number of pages
		$this->pages = ceil($this->length / $this->record_per_page);
		
		// Calculate the starting point 
		$this->start = ceil(($this->page - 1) * $this->record_per_page);
		
		// return the portion of results
		return array_slice($this->array, $this->start, $this->record_per_page);
	}
	/**
	* Get the query string result buider
	* 
	* @return buider string query
	* @access public 
	*/
	public function buildQuery() {
		$qs = ';
		if (!empty($_SERVER['QUERY_STRING'])) {
			$parts = explode("&", $_SERVER['QUERY_STRING']);
			$query_array = array();
			foreach ($parts as $val) {
				if (stristr($val, 'page') == false)  {
					array_push($query_array, $val);
				}
			}
			if (count($query_array) != 0) {
				$qs = "&".implode("&", $query_array);
			}  
		}
		return $qs; 
	}
	/**
	* Get the html links for the generated page offset
	* 
	* @param array $params A list of parameters (probably get/post) to
	* pass around with each request
	* @return mixed  Return description (if any) ...
	* @access public 
	*/
    public function getLinks($link) {
		$plinks = array();
		$links = array();
		$slinks = array();
      	
		// Concatenate the get variables to add to the page numbering string
		$queryUrl = $this->buildQuery();
		
		// If we have more then one pages
		if (($this->pages) > 1) {
			// Assign the 'previous page' link into the array if we are not on the first page
			if ($this->page != 1) {
				if ($this->_showFirstAndLast) {
					$plinks[] = ' <a rel="nofollow" href="'.$link.'/?page=1'.$queryUrl.'">&laquo;&laquo;</a> ';
				}
				$plinks[] = ' <a rel="nofollow" href="'.$link.'/?page='.($this->page - 1).$queryUrl.'" class="prev">&laquo;</a> ';
			}
		
			// Assign all the page numbers & links to the array
			for ($j = 1; $j < ($this->pages + 1); $j++) {
				if ($this->page == $j) {
					$links[] = ' <a rel="nofollow" class="current">'.$j.'</a> '; // If we are on the same page as the current item
				} else {
					$links[] = ' <a rel="nofollow" href="'.$link.'/?page='.$j.$queryUrl.'">'.$j.'</a> '; // add the link to the array
				}
			}
		
			// Assign the 'next page' if we are not on the last page
			if ($this->page < $this->pages) {
				$slinks[] = ' <a rel="nofollow" href="'.$link.'/?page='.($this->page + 1).$queryUrl.'" class="next">&raquo;</a> ';
				if ($this->_showFirstAndLast) {
					$slinks[] = ' <a rel="nofollow" href="'.$link.'?page='.($this->pages).$queryUrl.'">&raquo;&raquo; </a> ';
				}
			}
		
			// Push the array into a string using any some glue
			return implode(' ', $plinks).implode($this->mainSeperator, $links).implode(' ', $slinks);
		}
		return;
    }
}
?>

Bạn nhận ra rằng điểm mấu chốt của lớp phân trang này là hàm getResults(). Về mặt tính toán điểm đầu và cuối thì hàm nàykhông có gì đặc biệt. Điều đặc biết là nó sử dụng hàm array_slide() để cắt mảng nguồn từ vị trí xác định với số records chp phép được hiển thị trên một trang được truyền vào.

public function getResults(){
	// Assign the page variable
	if (empty($this->current_page) !== false) {
		$this->page = $this->current_page; // using the get method
	} else {
		$this->page = 1; // if we don't have a page number then assume we are on the first page
	}
	
	// Take the length of the array
	$this->length = count($this->array);
	
	// Get the number of pages
	$this->pages = ceil($this->length / $this->record_per_page);
	
	// Calculate the starting point 
	$this->start = ceil(($this->page - 1) * $this->record_per_page);
	
	// return the portion of results
	return array_slice($this->array, $this->start, $this->record_per_page);
}

Tiếp tục là làm sao để áp dụng pagination.class.php này vào trong ứng dụng cuả bạn.

Trong file index.php mình sẽ khởi tạo một cái mảng dữ liệu.

<?php 
	$data = array();
	$data[] = 'MVC CMS - Xây dựng thư viện xử lý lớp Session';
	$data[] = 'Nhân bản đối tượng trong PHP';
	$data[] = 'MVC CMS- Xây dựng thư viện xử lý template';
	$data[] = 'Bài 13: Phép Join trong MySQL';
	$data[] = 'MVC CMS - Tích hợp smarty template vào ứng dụng';
	$data[] = 'MVC CMS - Thư viện load controller';
	$data[] = 'MVC CMS - Viết thư viện Get config';
	$data[] = 'MVC CMS - Mô hình hoạt động của TzCMS';
	$data[] = 'MVC CMS - Cấu trúc folder CMS';
	$data[] = 'Xây dựng ứng dụng theo mô hình MVC';
	$data[] = 'MVC PHP - Giới thiệu về MVC';
	$data[] = 'Gọi hàm Javascript sử dụng biến tên của hàm';
	$data[] = 'Tạo dropdown timezone PHP';
	$data[] = 'Bài 20: Câu lệnh CASE trong MySQL';
	$data[] = 'Share tool HTML buider v2.28';
	$data[] = 'Hiệu ứng pre-loader animation facebook';
	$data[] = 'Làm menu ẩn hiện khi scroll bằng JQuery - Headroom';
	$data[] = 'Xây dựng class phân trang OPP';
	$data[] = 'Hiệu ứng sản phẩm bay vào giỏ hàng sử dụng JQuery';
	#
	require_once('pagination.class.php');
	$record_per_page = 5;
	$current_page = isset($_GET['page']) && intval($_GET['page']) ? intval($_GET['page']) : 1;
	$link = '/demo/php-pagination-array';
	
	// Khởi tạo đối tượng
	$pagination = new paginationArray($data, $current_page, $record_per_page, array('a'=>1));
	// Set Prev và Last
	$pagination->setShowFirstAndLast(TRUE);
	$lstItems = $pagination->getResults();
	$pageHTML = $pagination->getLinks($link);
?>

 <table class="tbl-grid" cellpadding="0" cellspacing="0" awidth="100%">
	<thead><tr>
		<td align="center" class="gridheader">STT</td>
		<td class="gridheader">Tiêu đề</td>
	</tr></thead>
	<tbody>
		<?php for($i=0; $i<count($lstItems); $i++){ ?>
		<tr>
			<td align="center"><?php echo $i; ?></td>
			<td><?php echo $lstItems[$i]; ?></td>
		</tr>
		<?php } ?>
	</tbody>
</table>
<div class="pagination">
	<?php echo $pageHTML; ?>
</div>

Tổng kết.

Như vậy trong bài viết này mình đã hướng dẫn các bạn phân trang một mảng trong PHP. Có thể đây là một class khá là basic nó chưa phân đoạn và các bạn có thể hoàn thiện thêm để có một class phần trang theo ý muốn.
0