Logic behind pagination like google

ThomasK picture ThomasK · Jun 30, 2012 · Viewed 20.3k times · Source

What is the logic behind google's pagination behaviour?

My paginator goes something like this:

[1]  2   3  ...  184   >
 <   1  [2]  3   4  ...  184   >
 <   1   2  [3]  4   5  ...  184   >
 <   1   2   3  [4]  5   6   ...  184   >
 <   1  ...  3   4  [5]  6    7   ...  184   >
 <   1  ...  4   5  [6]  7    8   ...  184   >
 <   1  ...  5   6  [7]  8    9   ...  184   >
 <   1  ...  6   7  [8]  9    10  ...  184   >

Here is a live version of the above example: http://www.dev.thomaskile.me/?page=test-zone&module=Paginator.
I know why this is happening; I've set the amount of page numbers to be shown on each side of current page to two (2).

I would rather have the range of numbers to be equal like this:

[1]  2   3   4   5   6   7   8   ...   184   >
 <   1  [2]  3   4   5   6   7   ...   184   >
 <   1   2  [3]  4   5   6   7   ...   184   >
 <   1   2   3  [4]  5   6   7   ...   184   >
 <   1  ...  3   4  [5]  6   7   ...   184   >
 <   1  ...  4   5  [6]  7   8   ...   184   >
 <   1  ...  5   6  [7]  8   9   ...   184   >    
 <   1  ...  6   7  [8]  9   10  ...   184   >

It's at the beginning and the end I need to make some changes, but can't figure out how to make it an easy operation...
I would like to make it flexible as well. Meaning I would like to be able to change the number of wanted pages on each side, and have the script expand and calculate it all...

Here is my code so far:

/**
 *  page controller buttons 
 *  @param str $this->querySting      href="URL string"
 *  @param str $this->pageIdentifier  $_GET['this-name']
 *  @param int $this->numPages        Total amount of pages
 *  @param int $this->midRange        Number of pages to show on each side of current page
 */

public function prevPage() 
{
    if ($this->currentPage > 1){ 
        $prevPage = ($this->currentPage - 1); 
        return '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$prevPage.'" class="prev">prev</a>'; 
    }
}
public function nextPage() 
{
    if ($this->currentPage < $this->numPages) { 
        $nextPage = $this->currentPage + 1;
        return '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$nextPage.'" class="next">next</a>';  
    }  
}
public function firstPage() 
{
    if ($this->currentPage > ($this->midRange + 1)) {  //  if number of pages between "currentPage" and "firstPage" exceeds $midRange with 1...
        $firstPage .= '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'=1" class="first">1</a>';  //  ...show "first page"-link
        if ($this->currentPage > ($this->midRange + 2)) {   //  if number of pages between $currentPage and "first page" exceeds $midRange with more than 1
            $firstPage .= '&hellip;';  //  add "..." between "1st page"-link and first page in $range
        }
    }
    return $firstPage;
}
public function lastPage() 
{
    if ($this->currentPage < ($this->numPages - $this->midRange)) {  //  if number of pages between "currentPage" and "last page" is equal to $midRange
        if (($this->currentPage < ($this->numPages - $this->midRange) - 1)) {  //  if number of pages between $currentPage and "last page" exceeds $range with more than two
            $lastPage .= '&hellip;';  //  add "..." between "last page"-link and last page in $range
        } 
        $lastPage .= '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$this->numPages.'" class="last">'.$this->numPages.'</a>';   //  show "last page"-link
    }
    return $lastPage;
}

#  Range of pages between (prev first ...) and (... last next)
public function listPages() 
{
    for ($i = ($this->currentPage - $this->midRange); $i < (($this->currentPage + $this->midRange) + 1); $i++){
       if (($i > 0) && ($i <= $this->numPages))  //  if page number are within page range
       {
          if ($i == $this->currentPage) { $listPages .= '<a class="current">'.$i.'</a>'; }  //  if we're on current page
          else { $listPages .= '<a href="'.$this->queryString.'&'.$this->pageIdentifier.'='.$i.'">'.$i.'</a>'; }  //  if not current page
        }
    }
    return $listPages; 
}

Answer

invisal picture invisal · Jun 30, 2012

This is what I do for my Pagination.

$startPage = $currentPage - 4;
$endPage = $currentPage + 4;

if ($startPage <= 0) {
    $endPage -= ($startPage - 1);
    $startPage = 1;
}

if ($endPage > $totalPage)
    $endPage = $totalPage;

if ($startPage > 1) echo " First ... ";
for($i=$startPage; $i<=$endPage; $i++) echo " {$i} ";
if ($endPage < $totalPage) echo " ... Last ";

I believe my code is self-explained, but I will try to explain it in plain English. First of all, you need to know two things before you can generate Pagination: $totalPage and $currentPage.

Step 1: Assuming that the current page is in mid-range. $startPage and $endPage store range of page that pagination try to generate.

Step 2: If $startPage is negative, then you need to make-up for $endPage.

Step 3: If $endPage excess $totalPage, then $endPage is the last page.

Step 4: Generating Pagination into HTML. (it is up to you how you want your pagination to look. I will simply use plain text to represent my pagination)

if ($startPage > 1) echo " First ... ";
for($i=$startPage; $i<=$endPage; $i++) echo " {$i} ";
if ($endPage < $totalPage) echo " ... Last ";

Fixed flaw to my previous logic

$startPage = ($curPage < 5)? 1 : $curPage - 4;
$endPage = 8 + $startPage;
$endPage = ($totalPage < $endPage) ? $totalPage : $endPage;
$diff = $startPage - $endPage + 8;
$startPage -= ($startPage - $diff > 0) ? $diff : 0;

if ($startPage > 1) echo " First ... ";
for($i=$startPage; $i<=$endPage; $i++) echo " {$i} ";
if ($endPage < $totalPage) echo " ... Last ";