Commit 927e675c authored by Taylor Otwell's avatar Taylor Otwell

refactor the paginator to reincorporate the google style slider.

parent fcf9cd3c
......@@ -54,11 +54,11 @@ class Paginator {
protected $appendage;
/**
* The pagination elements that will be generated.
* The "dots" element used in the pagination slider.
*
* @var array
* @var string
*/
protected $elements = array('first', 'previous', 'status', 'next', 'last');
protected $dots = '<span class="dots">...</span>';
/**
* Create a new Paginator instance.
......@@ -91,9 +91,9 @@ class Paginator {
{
$page = static::page($total, $per_page);
$last_page = ceil($total / $per_page);
$last = ceil($total / $per_page);
return new static($results, $page, $total, $per_page, $last_page);
return new static($results, $page, $total, $per_page, $last);
}
/**
......@@ -116,198 +116,266 @@ class Paginator {
return ($last > 0) ? $last : 1;
}
return ($page < 1 or filter_var($page, FILTER_VALIDATE_INT) === false) ? 1 : $page;
return (static::valid($page)) ? $page : 1;
}
/**
* Determine if a given page number is a valid page.
*
* A valid page must be greater than or equal to one and a valid integer.
*
* @param int $page
* @return bool
*/
protected static function valid($page)
{
return $page >= 1 and filter_var($page, FILTER_VALIDATE_INT) !== false;
}
/**
* Create the HTML pagination links.
*
* Typically, an intelligent, "sliding" window of links will be rendered based
* on the total number of pages, the current page, and the number of adjacent
* pages that should rendered. This creates a beautiful paginator similar to
* that of Google's.
*
* Example: 1 2 ... 23 24 25 [26] 27 28 29 ... 51 52
*
* If you wish to render only certain elements of the pagination control,
* explore some of the other public methods available on the instance.
*
* <code>
* // Render the pagination links
* echo $paginator->links();
*
* // Render the pagination links using a given window size
* echo $paginator->links(5);
* </code>
*
* @param int $adjacent
* @return string
*/
public function links()
public function links($adjacent = 3)
{
if ($this->last <= 1) return '';
// Each pagination element is created by an element method. This allows
// us to keep this class clean and simple, because pagination code can
// become a mess. We would rather keep it simple and beautiful.
// The hard-coded seven is to account for all of the constant elements in a
// sliding range, such as the current page, the two ellipses, and the two
// beginning and ending pages.
//
// If the page is greater the one, we will render the first and previous
// links, otherwise we skip them since we are already on the first page.
if ($this->page > 1)
// If there are not enough pages to make the creation of a slider possible
// based on the adjacent pages, we will simply display all of the pages.
// Otherwise, we will create a "truncating" slider which displays a nice
// window of pages based on the current page.
if ($this->last < 7 + ($adjacent * 2))
{
$elements[] = $this->first();
$elements[] = $this->previous();
$numbers = $this->range(1, $this->last);
}
// The status is always rendered regardless of the current page. So we
// can simply add it to the array of pagination elements.
$elements[] = $this->status();
// If the current page is not the last page, we will render the next
// and last links. Otherwise we will skip them since we are already
// on the last page and can't go any further.
if ($this->page < $this->last)
else
{
$elements[] = $this->next();
$elements[] = $this->last();
$numbers = $this->slider($adjacent);
}
return '<div class="pagination">'.implode(' ', $elements).'</div>'.PHP_EOL;
$content = $this->previous().' '.$numbers.' '.$this->next();
return '<div class="pagination">'.$content.'</div>';
}
/**
* Get the "status" pagination element.
* Build sliding list of HTML numeric page links.
*
* @param string $text
* This method is very similar to the "links" method, only it does not
* render the "first" and "last" pagination links, but only the pages.
*
* <code>
* // Render the pagination slider
* echo $paginator->slider();
*
* // Render the pagination slider using a given window size
* echo $paginator->slider(5);
* </code>
*
* @param int $adjacent
* @return string
*/
public function status($text = null)
public function slider($adjacent = 3)
{
if (is_null($text)) $text = Lang::line('pagination.status')->get();
$window = $adjacent * 2;
return str_replace(array(':current', ':last'), array($this->page, $this->last), $text);
}
// Example: 1 [2] 3 4 5 6 ... 23 24
if ($this->page <= $window)
{
return $this->range(1, $window + 2).' '.$this->ending();
}
/**
* Create the "first" pagination element.
*
* @param string $text
* @return string
*/
public function first($text = null)
{
return $this->backwards(__FUNCTION__, $text, 1);
// Example: 1 2 ... 32 33 34 35 [36] 37
elseif ($this->page >= $this->last - $window)
{
return $this->beginning().' '.$this->range($this->last - $window - 2, $this->last);
}
// Example: 1 2 ... 23 24 25 [26] 27 28 29 ... 51 52
$content = $this->range($this->page - $adjacent, $this->page + $adjacent);
return $this->beginning().' '.$content.' '.$this->ending();
}
/**
* Create the "previous" pagination element.
* Generate the "previous" HTML link.
*
* <code>
* // Create the "previous" pagination element
* echo $paginator->previous();
*
* // Create the "previous" pagination element with custom text
* echo $paginator->previous('Go Back');
* </code>
*
* @param string $text
* @return string
*/
public function previous($text = null)
{
return $this->backwards(__FUNCTION__, $text, $this->page - 1);
$disabled = function($page) { return $page <= 1; };
return $this->element(__FUNCTION__, $this->page - 1, $text, $disabled);
}
/**
* Create the "next" pagination element.
* Generate the "next" HTML link.
*
* <code>
* // Create the "next" pagination element
* echo $paginator->next();
*
* // Create the "next" pagination element with custom text
* echo $paginator->next('Skip Forwards');
* </code>
*
* @param string $text
* @return string
*/
public function next($text = null)
{
return $this->forwards(__FUNCTION__, $text, $this->page + 1);
$disabled = function($page, $last) { return $page >= $last; };
return $this->element(__FUNCTION__, $this->page + 1, $text, $disabled);
}
/**
* Create the "last" pagination element.
* Create a chronological pagination element, such as a "previous" or "next" link.
*
* @param string $text
* @param string $element
* @param int $page
* @param string $text
* @param Closure $disabled
* @return string
*/
public function last($text = null)
protected function element($element, $page, $text, $disabled)
{
return $this->forwards(__FUNCTION__, $text, $this->last);
$class = "{$element}_page";
if (is_null($text)) $text = Lang::line("pagination.{$element}")->get();
// Each consumer of this method provides a "disabled" Closure which can
// be used to determine if the element should be a span element or an
// actual link. For example, if the current page is the first page,
// the "first" element should be a span instead of a link.
if ($disabled($this->page, $this->last))
{
return HTML::span($text, array('class' => "{$class} disabled"));
}
else
{
return $this->link($page, $text, $class);
}
}
/**
* Create a "backwards" paginatino element.
*
* This function handles the creation of the first and previous elements.
* Build the first two page links for a sliding page range.
*
* @param string $element
* @param string $text
* @param int $last
* @return string
*/
protected function backwards($element, $text, $last)
protected function beginning()
{
$disabler = function($page) { return $page <= 1; };
return $this->element($element, $text, $last, $disabler);
return $this->range(1, 2).' '.$this->dots;
}
/**
* Create a "forwards" paginatino element.
*
* This function handles the creation of the next and last elements.
* Build the last two page links for a sliding page range.
*
* @param string $element
* @param string $text
* @param int $last
* @return string
*/
protected function forwards($element, $text, $last)
protected function ending()
{
$disabler = function($page, $last) { return $page >= $last; };
return $this->element($element, $text, $last, $disabler);
return $this->dots.' '.$this->range($this->last - 1, $this->last);
}
/**
* Create a chronological pagination element.
* Build a range of numeric pagination links.
*
* @param string $element
* @param string $text
* @param int $page
* @param Closure $disabler
* For the current page, an HTML span element will be generated instead of a link.
*
* @param int $start
* @param int $end
* @return string
*/
protected function element($element, $text, $page, $disabler)
protected function range($start, $end)
{
$class = "{$element}_page";
if (is_null($text)) $text = Lang::line("pagination.{$element}")->get();
$pages = array();
if ($disabler($this->page, $this->last))
for ($page = $start; $page <= $end; $page++)
{
return HTML::span($text, array('class' => "disabled {$class}"));
if ($this->page == $page)
{
$pages[] = HTML::span($page, array('class' => 'current'));
}
else
{
$pages[] = $this->link($page, $page, null);
}
}
else
{
// We will assume the page links should use HTTPS if the current request
// is also using HTTPS. Since pagination links automatically point to
// the current URI, this makes pretty good sense.
list($uri, $secure) = array(Request::uri(), Request::secure());
$appendage = '?page='.$page.$this->appendage($element, $page);
return HTML::link($uri.$appendage, $text, array('class' => $class), $secure);
}
return implode(' ', $pages);
}
/**
* Create the pagination link "appendage" for an element.
* Create a HTML page link.
*
* @param string $element
* @param int $page
* @param string $text
* @param string $attributes
* @return string
*/
protected function appendage($element, $page)
protected function link($page, $text, $class)
{
if ( ! is_null($this->appendage)) return $this->appendage;
$url = URI::current().'?page='.$page.$this->appendage($this->appends);
$appendage = '';
return HTML::link($url, $text, compact('class'), Request::secure());
}
if (count($this->appends) > 0)
/**
* Create the "appendage" that should be attached to every pagination link.
*
* The developer may assign an array of values that will be converted to a
* query string and attached to every pagination link. This allows simple
* implementation of sorting or other things the developer may need.
*
* @param array $appends
* @return string
*/
protected function appendage($appends)
{
if ( ! is_null($this->appendage))
{
$appendage .= '&'.http_build_query($this->appends);
return $this->appendage;
}
return $this->appendage = $appendage;
return $this->appendage = (count($appends) > 0) ? '&'.http_build_query($appends) : '';
}
/**
* Set the items that should be appended to the link query strings.
*
* This provides a convenient method of maintaining sort or passing other
* information to the route handling pagination.
*
* @param array $values
* @return Paginator
*/
......@@ -317,18 +385,4 @@ class Paginator {
return $this;
}
/**
* Set the elements that should be included when creating the pagination links.
*
* The available elements are "first", "previous", "status", "next", and "last".
*
* @param array $elements
* @return string
*/
public function elements($elements)
{
$this->elements = $elements;
return $this;
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment