65.9K
CodeProject is changing. Read more.
Home

Dynamic Pagination

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.22/5 (9 votes)

Jan 25, 2010

CPOL
viewsIcon

14071

More validation can be applied when handling default values. I am not a fan of outputing anything directly from a class, I prefer to return the links as an array. It's easier to maintain. This way, when/if you change the styling; then it saves having to trawl through your classes.<?phpclass...

More validation can be applied when handling default values. I am not a fan of outputing anything directly from a class, I prefer to return the links as an array. It's easier to maintain. This way, when/if you change the styling; then it saves having to trawl through your classes.
<?php
class Pagination
{
	const DEFAULT_DISPLAYED_ROWS = 30;
	const DEFAULT_GET_INDEX = 'page';
	const DEFAULT_MAX_LINKS_DISPLAYED = 5;

	protected $get_index;
	protected $max_links_displayed;
	protected $total_number_of_records;

	protected $displayed_rows;
	public function get_displayed_rows()
	{
		return $this->displayed_rows;
	}
	
	protected $start;
	public function get_start()
	{
		return $this->start;
	}
	
	public function __construct( $total_number_of_records , $displayed_rows = null , $max_links_displayed = null , $get_index = null )
	{
		$this->total_number_of_records = is_int( $total_number_of_records ) && $this->total_number_of_records >= 0 ? $total_number_of_records : null;
		if ( !isset( $this->total_number_of_records ) )
			throw new Exception( '$total_number_of_records must be an int and greater than or equal to 0' );
		
		// Check is $displayed_rows is set and a int, otherwise it is the default.
		$this->displayed_rows = isset( $displayed_rows ) && is_int( $displayed_rows ) ? $displayed_rows : self::DEFAULT_DISPLAYED_ROWS;
		
		// Check is $max_links_displayed is set and an int, otherwise it is the default.
		$this->max_links_displayed = isset( $max_links_displayed ) && is_int( $max_links_displayed ) && $max_links_displayed >= 0
						? $max_links_displayed : self::DEFAULT_MAX_LINKS_DISPLAYED;

		// Check is $get_index is set and a string, otherwise it is the default.
		$this->get_index = isset( $get_index ) && is_string( $get_index ) ? $get_index : self::DEFAULT_GET_INDEX;
		
		// Check the if the page start ($_GET[$this->get_index]) is set, int, greater than 0 and less than $total_number_of_records.
		$this->start = isset( $_GET[ $this->get_index ] ) && is_int( (int)$_GET[ $this->get_index ] ) && $_GET[ $this->get_index ] >= 0
						&& $_GET[ $this->get_index ] < $total_number_of_records ? $_GET[ $this->get_index ] : 0;
		
		// Round down to a mulitple of $this->displayed_rows.
		$this->start = $this->start >= $this->displayed_rows ? $this->round_to_multiple( $this->displayed_rows , $this->start ) : 0;
	}

	public function get_paginate_links()
	{
		// Get a clean URL.
		$url = isset( $_GET[ $this->get_index ] ) ? str_ireplace( array( '?' . $this->get_index . '=' . $_GET[ $this->get_index ] ,
						'&' . $this->get_index . '=' . $_GET[ $this->get_index ] ) , '' , $_SERVER[ 'REQUEST_URI' ] ) : $_SERVER[ 'REQUEST_URI' ];
		$url = htmlentities( $url . ( ( substr( $url , -4 ) == '.php' || substr( $url , -1 ) == '/' ) ? '?' : '&' ) . $this->get_index . '=' );

		$links = array();
		
		// If the current paginate is not the first.
		if ( $this->start > 0 )
		{
			$links[] = $this->paginate_link( 'First' , $url . '0' );
			$links[] = $this->paginate_link( 'Prev' , $url . ( $this->start - $this->displayed_rows ) );
		}

		// The last multiple to be allowed to display.
		$last = $this->round_to_multiple( $this->displayed_rows , $this->total_number_of_records - 1);
		
		// Get the page currently on.
		$current_page = ( $this->start / $this->displayed_rows ) + 1;
		
		// Get the last page number to be displayed.
		$last_page = ( $last / $this->displayed_rows ) + 2;

		// Get the page to start counting from.
		$start_page = $current_page > 0 ? $current_page - ( floor( $this->max_links_displayed / 2 ) - ( $this->max_links_displayed % 2 == 0 ? 1 : 0 ) ) : 1;
		$start_page = $start_page + $this->max_links_displayed > $last_page ? $last_page - $this->max_links_displayed : $start_page;
		
		$links_displayed = 0;
		while ( $links_displayed < $this->max_links_displayed )
		{
			// If the last page is reached before the maximum links have been displayed.
			if ( $start_page + $links_displayed == $last_page )
				break;

			// If the current page (in the loop) is not in arrears (negative).
			if ( $start_page + $links_displayed > 0 )
			{
				if ( $start_page + $links_displayed == $current_page )
					$links[] = $this->paginate_link( $start_page + $links_displayed , '#', true );
				else
					$links[] = $this->paginate_link( $start_page + $links_displayed , $url . ( ($start_page + $links_displayed - 1) * $this->displayed_rows ) );
			}
			else
				$this->max_links_displayed++;

			$links_displayed++;
		}

		// If the current page is not the last.
		if ( $current_page < $last_page - 1 )
		{
			$links[] = $this->paginate_link( 'Next' , $url . ( $this->start + $this->displayed_rows ) );
			$links[] = $this->paginate_link( 'Last' , $url . $last );
		}
		
		return $links;
	}

	private function paginate_link( $name , $href , $current = null )
	{
		if ( isset( $current ) )
			return array( 'name' => $name , 'href' => $href , 'current' => $current );
		else
			return array( 'name' => $name , 'href' => $href );
	}

	private function round_to_multiple( $factor , $value )
	{
		return $value % $factor == 0 ? $value : ( ceil( $value / $factor ) - 1 ) * $factor;
	}
}
?>
To be called similarly by your method.
<?php

$Pagination = new Pagination( 10 , 2 , 3 );

// ...
// mysql_query( 'SELECT * FROM table LIMIT ' . $Pagination->get_start() . ', ' . $Pagination->get_displayed_rows() );
// ...

foreach ( $Pagination->get_paginate_links() as $link )
{
	echo '<a href="' . $link[ 'href' ] . '"' . ( isset( $link[ 'current' ] ) ? 'style="text-decoration: none;"' : '' ) . '>' . $link[ 'name' ] . '</a> ';
}

?>