init
@@ -0,0 +1,5 @@
|
||||
/vendor/
|
||||
composer.lock
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
@@ -0,0 +1,14 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- hhvm
|
||||
|
||||
|
||||
before_script: composer install --dev --prefer-source
|
||||
|
||||
notifications:
|
||||
email: jason@grimesit.com
|
||||
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Jason Grimes
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
PHP Paginator
|
||||
=============
|
||||
|
||||
[](https://travis-ci.org/jasongrimes/php-paginator)
|
||||
[](https://packagist.org/packages/jasongrimes/paginator)
|
||||
[](https://packagist.org/packages/jasongrimes/paginator) [](https://packagist.org/packages/jasongrimes/paginator) [](https://packagist.org/packages/jasongrimes/paginator)
|
||||
|
||||
A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. The "first" and "last" page links are shown inline as page numbers, and excess page numbers are replaced by ellipses.
|
||||
|
||||
## Screenshots
|
||||
|
||||
These examples show how the paginator handles overflow when there are a lot of pages.
|
||||
They're rendered using the sample templates provided in the [examples](examples/) directory,
|
||||
which depend on Twitter Bootstrap.
|
||||
You can easily use your own custom HTML to render the pagination control instead.
|
||||
|
||||
Default template:
|
||||
|
||||
<img src="examples/screenshot-default-first.png" width="447"><br/>
|
||||
<img src="examples/screenshot-default-mid.png" width="597"><br/>
|
||||
<img src="examples/screenshot-default-last.png" width="534"><br/>
|
||||
|
||||
Small template (useful for mobile interfaces):
|
||||
|
||||
<img src="examples/screenshot-small-first.png" width="157"><br/>
|
||||
<img src="examples/screenshot-small-mid.png" width="220"><br/>
|
||||
<img src="examples/screenshot-small-last.png" width="157"><br/>
|
||||
|
||||
The small template renders the page number as a select list to save space:
|
||||
|
||||
<img src="examples/screenshot-small-mid-open.png" width="218">
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Install with composer:
|
||||
|
||||
composer require "jasongrimes/paginator:~1.0"
|
||||
|
||||
## Basic usage
|
||||
|
||||
Here's a quick example using the defaults:
|
||||
|
||||
<?php
|
||||
|
||||
require '../vendor/autoload.php';
|
||||
|
||||
use JasonGrimes\Paginator;
|
||||
|
||||
$totalItems = 1000;
|
||||
$itemsPerPage = 50;
|
||||
$currentPage = 8;
|
||||
$urlPattern = '/foo/page/(:num)';
|
||||
|
||||
$paginator = new Paginator($totalItems, $itemsPerPage, $currentPage, $urlPattern);
|
||||
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<!-- The default, built-in template supports the Twitter Bootstrap pagination styles. -->
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
// Example of rendering the pagination control with the built-in template.
|
||||
// See below for information about using other templates or custom rendering.
|
||||
|
||||
echo $paginator;
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
This will output the following:
|
||||
|
||||
<img src="examples/screenshot-default-mid.png" width="597">
|
||||
|
||||
<ul class="pagination">
|
||||
<li><a href="/foo/page/7">« Previous</a></li>
|
||||
<li><a href="/foo/page/1">1</a></li>
|
||||
<li class="disabled"><span>...</span></li>
|
||||
<li><a href="/foo/page/5">5</a></li>
|
||||
<li><a href="/foo/page/6">6</a></li>
|
||||
<li><a href="/foo/page/7">7</a></li>
|
||||
<li class="active"><a href="/foo/page/8">8</a></li>
|
||||
<li><a href="/foo/page/9">9</a></li>
|
||||
<li><a href="/foo/page/10">10</a></li>
|
||||
<li><a href="/foo/page/11">11</a></li>
|
||||
<li><a href="/foo/page/12">12</a></li>
|
||||
<li class="disabled"><span>...</span></li>
|
||||
<li><a href="/foo/page/20">20</a></li>
|
||||
<li><a href="/foo/page/9">Next »</a></li>
|
||||
</ul>
|
||||
|
||||
To render it with one of the other example templates, just make sure the variable is named `$paginator` and then include the template file:
|
||||
|
||||
$paginator = new Paginator($totalItems, $itemsPerPage, $currentPage, $urlPattern);
|
||||
|
||||
include '../vendor/jasongrimes/paginator/examples/pagerSmall.phtml';
|
||||
|
||||
<img src="examples/screenshot-small-mid.png" width="220"><br/>
|
||||
|
||||
If the example templates don't suit you, you can iterate over the paginated data to render your own pagination control.
|
||||
|
||||
## Rendering a custom pagination control
|
||||
|
||||
Use `$paginator->getPages()`, `$paginator->getNextUrl()`, and `$paginator->getPrevUrl()` to render a pagination control with your own HTML.
|
||||
For example:
|
||||
|
||||
<ul class="pagination">
|
||||
<?php if ($paginator->getPrevUrl()): ?>
|
||||
<li><a href="<?php echo $paginator->getPrevUrl(); ?>">« Previous</a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($paginator->getPages() as $page): ?>
|
||||
<?php if ($page['url']): ?>
|
||||
<li <?php echo $page['isCurrent'] ? 'class="active"' : ''; ?>>
|
||||
<a href="<?php echo $page['url']; ?>"><?php echo $page['num']; ?></a>
|
||||
</li>
|
||||
<?php else: ?>
|
||||
<li class="disabled"><span><?php echo $page['num']; ?></span></li>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($paginator->getNextUrl()): ?>
|
||||
<li><a href="<?php echo $paginator->getNextUrl(); ?>">Next »</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<?php echo $paginator->getTotalItems(); ?> found.
|
||||
|
||||
Showing
|
||||
<?php echo $paginator->getCurrentPageFirstItem(); ?>
|
||||
-
|
||||
<?php echo $paginator->getCurrentPageLastItem(); ?>.
|
||||
</p>
|
||||
|
||||
|
||||
See the [examples](examples) directory for more sample templates.
|
||||
|
||||
## Pages data structure
|
||||
|
||||
$paginator->getPages();
|
||||
|
||||
`getPages()` returns a data structure like the following:
|
||||
|
||||
array (
|
||||
array ('num' => 1, 'url' => '/foo/page/1', 'isCurrent' => false),
|
||||
array ('num' => '...', 'url' => NULL, 'isCurrent' => false),
|
||||
array ('num' => 5, 'url' => '/foo/page/5', 'isCurrent' => false),
|
||||
array ('num' => 6, 'url' => '/foo/page/6', 'isCurrent' => false),
|
||||
array ('num' => 7, 'url' => '/foo/page/7', 'isCurrent' => false),
|
||||
array ('num' => 8, 'url' => '/foo/page/8', 'isCurrent' => true),
|
||||
array ('num' => 9, 'url' => '/foo/page/9', 'isCurrent' => false),
|
||||
array ('num' => 10, 'url' => '/foo/page/10', 'isCurrent' => false),
|
||||
array ('num' => 11, 'url' => '/foo/page/11', 'isCurrent' => false),
|
||||
array ('num' => 12, 'url' => '/foo/page/12', 'isCurrent' => false),
|
||||
array ('num' => '...', 'url' => NULL, 'isCurrent' => false),
|
||||
array ('num' => 20, 'url' => '/foo/page/20', 'isCurrent' => false),
|
||||
)
|
||||
|
||||
## Customizing the number of pages shown
|
||||
|
||||
By default, no more than 10 pages are shown, including the first and last page, with the overflow replaced by ellipses.
|
||||
To change the default number of pages:
|
||||
|
||||
$paginator->setMaxPagesToShow(5);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "jasongrimes/paginator",
|
||||
"type": "library",
|
||||
"description": "A lightweight PHP paginator, for generating pagination controls in the style of Stack Overflow and Flickr. The 'first' and 'last' page links are shown inline as page numbers, and excess page numbers are replaced by ellipses.",
|
||||
"keywords": ["paginator", "pagination", "pager"],
|
||||
"homepage": "http://github.com/jasongrimes/php-paginator",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jason Grimes",
|
||||
"email": "jason@grimesit.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"JasonGrimes": "src/"}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-0": {"JasonGrimes\\Tests": "tests/"}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php if ($paginator->getNumPages() > 1): ?>
|
||||
<ul class="pagination">
|
||||
<?php if ($paginator->getPrevUrl()): ?>
|
||||
<li><a href="<?php echo $paginator->getPrevUrl(); ?>">« Previous</a></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($paginator->getPages() as $page): ?>
|
||||
<?php if ($page['url']): ?>
|
||||
<li <?php echo $page['isCurrent'] ? 'class="active"' : ''; ?>>
|
||||
<a href="<?php echo $page['url']; ?>"><?php echo $page['num']; ?></a>
|
||||
</li>
|
||||
<?php else: ?>
|
||||
<li class="disabled"><span><?php echo $page['num']; ?></span></li>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($paginator->getNextUrl()): ?>
|
||||
<li><a href="<?php echo $paginator->getNextUrl(); ?>">Next »</a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
@@ -0,0 +1,19 @@
|
||||
{% if paginator.numPages > 1 %}
|
||||
<ul class="pagination">
|
||||
{% if paginator.prevUrl %}
|
||||
<li><a href="{{ paginator.prevUrl }}">« Previous</a></li>
|
||||
{% endif %}
|
||||
|
||||
{% for page in paginator.pages %}
|
||||
{% if page.url %}
|
||||
<li {{ page.isCurrent ? 'class="active"' : '' }}><a href="{{ page.url }}">{{ page.num }}</a></li>
|
||||
{% else %}
|
||||
<li class="disabled"><span>{{ page.num }}</span></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if paginator.nextUrl %}
|
||||
<li><a href="{{ paginator.nextUrl }}">Next »</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php if ($paginator->getNumPages() > 1): ?>
|
||||
<div class="input-group" style="width: 1px;">
|
||||
<?php if ($paginator->getPrevUrl()): ?>
|
||||
<span class="input-group-btn">
|
||||
<a href="<?php echo $paginator->getPrevUrl(); ?>" class="btn btn-default" type="button">« Prev</a>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<select class="form-control paginator-select-page" style="width: auto; cursor: pointer; -webkit-appearance: none; -moz-appearance: none; appearance: none;">
|
||||
<?php foreach ($paginator->getPages() as $page): ?>
|
||||
<?php if ($page['url']): ?>
|
||||
<option value="<?php echo $page['url']; ?>"<?php if ($page['isCurrent']) echo ' selected'; ?>>
|
||||
Page <?php echo $page['num']; ?>
|
||||
</option>
|
||||
<?php else: ?>
|
||||
<option disabled><?php echo $page['num']; ?></option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<?php if ($paginator->getNextUrl()): ?>
|
||||
<span class="input-group-btn">
|
||||
<a href="<?php echo $paginator->getNextUrl(); ?>" class="btn btn-default" type="button">Next »</a>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* Depends on a little bit of javascript like this being included somewhere (this example requires jquery):
|
||||
$(function() {
|
||||
$('.paginator-select-page').on('change', function() {
|
||||
document.location = $(this).val();
|
||||
});
|
||||
// Workaround to prevent iOS from zooming the page when clicking the select list:
|
||||
$('.paginator-select-page')
|
||||
.on('focus', function() {
|
||||
if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
|
||||
$(this).css('font-size', '16px');
|
||||
}
|
||||
})
|
||||
.on('blur', function() {
|
||||
if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
|
||||
$(this).css('font-size', '');
|
||||
}
|
||||
})
|
||||
;
|
||||
});
|
||||
*/ ?>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{% if paginator.numPages > 1 %}
|
||||
<div class="input-group" style="width: 1px;">
|
||||
{% if paginator.prevUrl %}
|
||||
<span class="input-group-btn">
|
||||
<a href="{{ paginator.prevUrl }}" class="btn btn-default" type="button">« Prev</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<select class="form-control paginator-select-page" style="width: auto; cursor: pointer; -webkit-appearance: none; -moz-appearance: none; appearance: none;">
|
||||
{% for page in paginator.pages %}
|
||||
{% if page.url %}
|
||||
<option value="{{ page.url }}"{{ page.isCurrent ? 'selected' : '' }}>
|
||||
Page {{ page.num }}
|
||||
</option>
|
||||
{% else %}
|
||||
<option disabled>{{ page.num }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
{% if paginator.nextUrl %}
|
||||
<span class="input-group-btn">
|
||||
<a href="{{ paginator.nextUrl }}" class="btn btn-default" type="button">Next »</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Depends on this little bit of javascript being included somewhere:
|
||||
$(function() {
|
||||
$('.paginator-select-page').on('change', function() {
|
||||
document.location = $(this).val();
|
||||
});
|
||||
// Workaround to prevent iOS from zooming the page when clicking the select list:
|
||||
$('.paginator-select-page')
|
||||
.on('focus', function() {
|
||||
if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
|
||||
$(this).css('font-size', '16px');
|
||||
}
|
||||
})
|
||||
.on('blur', function() {
|
||||
if (/(iPad|iPhone|iPod)/g.test(navigator.userAgent)) {
|
||||
$(this).css('font-size', '');
|
||||
}
|
||||
})
|
||||
;
|
||||
});
|
||||
#}
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
namespace JasonGrimes;
|
||||
|
||||
class Paginator
|
||||
{
|
||||
const NUM_PLACEHOLDER = '(:num)';
|
||||
|
||||
protected $totalItems;
|
||||
protected $numPages;
|
||||
protected $itemsPerPage;
|
||||
protected $currentPage;
|
||||
protected $urlPattern;
|
||||
protected $maxPagesToShow = 10;
|
||||
protected $previousText = 'Previous';
|
||||
protected $nextText = 'Next';
|
||||
|
||||
/**
|
||||
* @param int $totalItems The total number of items.
|
||||
* @param int $itemsPerPage The number of items per page.
|
||||
* @param int $currentPage The current page number.
|
||||
* @param string $urlPattern A URL for each page, with (:num) as a placeholder for the page number. Ex. '/foo/page/(:num)'
|
||||
*/
|
||||
public function __construct($totalItems, $itemsPerPage, $currentPage, $urlPattern = '')
|
||||
{
|
||||
$this->totalItems = $totalItems;
|
||||
$this->itemsPerPage = $itemsPerPage;
|
||||
$this->currentPage = $currentPage;
|
||||
$this->urlPattern = $urlPattern;
|
||||
|
||||
$this->updateNumPages();
|
||||
}
|
||||
|
||||
protected function updateNumPages()
|
||||
{
|
||||
$this->numPages = ($this->itemsPerPage == 0 ? 0 : (int) ceil($this->totalItems/$this->itemsPerPage));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $maxPagesToShow
|
||||
* @throws \InvalidArgumentException if $maxPagesToShow is less than 3.
|
||||
*/
|
||||
public function setMaxPagesToShow($maxPagesToShow)
|
||||
{
|
||||
if ($maxPagesToShow < 3) {
|
||||
throw new \InvalidArgumentException('maxPagesToShow cannot be less than 3.');
|
||||
}
|
||||
$this->maxPagesToShow = $maxPagesToShow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxPagesToShow()
|
||||
{
|
||||
return $this->maxPagesToShow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $currentPage
|
||||
*/
|
||||
public function setCurrentPage($currentPage)
|
||||
{
|
||||
$this->currentPage = $currentPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentPage()
|
||||
{
|
||||
return $this->currentPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $itemsPerPage
|
||||
*/
|
||||
public function setItemsPerPage($itemsPerPage)
|
||||
{
|
||||
$this->itemsPerPage = $itemsPerPage;
|
||||
$this->updateNumPages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getItemsPerPage()
|
||||
{
|
||||
return $this->itemsPerPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $totalItems
|
||||
*/
|
||||
public function setTotalItems($totalItems)
|
||||
{
|
||||
$this->totalItems = $totalItems;
|
||||
$this->updateNumPages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalItems()
|
||||
{
|
||||
return $this->totalItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getNumPages()
|
||||
{
|
||||
return $this->numPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $urlPattern
|
||||
*/
|
||||
public function setUrlPattern($urlPattern)
|
||||
{
|
||||
$this->urlPattern = $urlPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUrlPattern()
|
||||
{
|
||||
return $this->urlPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pageNum
|
||||
* @return string
|
||||
*/
|
||||
public function getPageUrl($pageNum)
|
||||
{
|
||||
return str_replace(self::NUM_PLACEHOLDER, $pageNum, $this->urlPattern);
|
||||
}
|
||||
|
||||
public function getNextPage()
|
||||
{
|
||||
if ($this->currentPage < $this->numPages) {
|
||||
return $this->currentPage + 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPrevPage()
|
||||
{
|
||||
if ($this->currentPage > 1) {
|
||||
return $this->currentPage - 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getNextUrl()
|
||||
{
|
||||
if (!$this->getNextPage()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getPageUrl($this->getNextPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPrevUrl()
|
||||
{
|
||||
if (!$this->getPrevPage()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getPageUrl($this->getPrevPage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of paginated page data.
|
||||
*
|
||||
* Example:
|
||||
* array(
|
||||
* array ('num' => 1, 'url' => '/example/page/1', 'isCurrent' => false),
|
||||
* array ('num' => '...', 'url' => NULL, 'isCurrent' => false),
|
||||
* array ('num' => 3, 'url' => '/example/page/3', 'isCurrent' => false),
|
||||
* array ('num' => 4, 'url' => '/example/page/4', 'isCurrent' => true ),
|
||||
* array ('num' => 5, 'url' => '/example/page/5', 'isCurrent' => false),
|
||||
* array ('num' => '...', 'url' => NULL, 'isCurrent' => false),
|
||||
* array ('num' => 10, 'url' => '/example/page/10', 'isCurrent' => false),
|
||||
* )
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPages()
|
||||
{
|
||||
$pages = array();
|
||||
|
||||
if ($this->numPages <= 1) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ($this->numPages <= $this->maxPagesToShow) {
|
||||
for ($i = 1; $i <= $this->numPages; $i++) {
|
||||
$pages[] = $this->createPage($i, $i == $this->currentPage);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Determine the sliding range, centered around the current page.
|
||||
$numAdjacents = (int) floor(($this->maxPagesToShow - 3) / 2);
|
||||
|
||||
if ($this->currentPage + $numAdjacents > $this->numPages) {
|
||||
$slidingStart = $this->numPages - $this->maxPagesToShow + 2;
|
||||
} else {
|
||||
$slidingStart = $this->currentPage - $numAdjacents;
|
||||
}
|
||||
if ($slidingStart < 2) $slidingStart = 2;
|
||||
|
||||
$slidingEnd = $slidingStart + $this->maxPagesToShow - 3;
|
||||
if ($slidingEnd >= $this->numPages) $slidingEnd = $this->numPages - 1;
|
||||
|
||||
// Build the list of pages.
|
||||
$pages[] = $this->createPage(1, $this->currentPage == 1);
|
||||
if ($slidingStart > 2) {
|
||||
$pages[] = $this->createPageEllipsis();
|
||||
}
|
||||
for ($i = $slidingStart; $i <= $slidingEnd; $i++) {
|
||||
$pages[] = $this->createPage($i, $i == $this->currentPage);
|
||||
}
|
||||
if ($slidingEnd < $this->numPages - 1) {
|
||||
$pages[] = $this->createPageEllipsis();
|
||||
}
|
||||
$pages[] = $this->createPage($this->numPages, $this->currentPage == $this->numPages);
|
||||
}
|
||||
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a page data structure.
|
||||
*
|
||||
* @param int $pageNum
|
||||
* @param bool $isCurrent
|
||||
* @return Array
|
||||
*/
|
||||
protected function createPage($pageNum, $isCurrent = false)
|
||||
{
|
||||
return array(
|
||||
'num' => $pageNum,
|
||||
'url' => $this->getPageUrl($pageNum),
|
||||
'isCurrent' => $isCurrent,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function createPageEllipsis()
|
||||
{
|
||||
return array(
|
||||
'num' => '...',
|
||||
'url' => null,
|
||||
'isCurrent' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an HTML pagination control.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHtml()
|
||||
{
|
||||
if ($this->numPages <= 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$html = '<ul class="pagination">';
|
||||
if ($this->getPrevUrl()) {
|
||||
$html .= '<li><a href="' . htmlspecialchars($this->getPrevUrl()) . '">« '. $this->previousText .'</a></li>';
|
||||
}
|
||||
|
||||
foreach ($this->getPages() as $page) {
|
||||
if ($page['url']) {
|
||||
$html .= '<li' . ($page['isCurrent'] ? ' class="active"' : '') . '><a href="' . htmlspecialchars($page['url']) . '">' . htmlspecialchars($page['num']) . '</a></li>';
|
||||
} else {
|
||||
$html .= '<li class="disabled"><span>' . htmlspecialchars($page['num']) . '</span></li>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getNextUrl()) {
|
||||
$html .= '<li><a href="' . htmlspecialchars($this->getNextUrl()) . '">'. $this->nextText .' »</a></li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toHtml();
|
||||
}
|
||||
|
||||
public function getCurrentPageFirstItem()
|
||||
{
|
||||
$first = ($this->currentPage - 1) * $this->itemsPerPage + 1;
|
||||
|
||||
if ($first > $this->totalItems) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $first;
|
||||
}
|
||||
|
||||
public function getCurrentPageLastItem()
|
||||
{
|
||||
$first = $this->getCurrentPageFirstItem();
|
||||
if ($first === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$last = $first + $this->itemsPerPage - 1;
|
||||
if ($last > $this->totalItems) {
|
||||
return $this->totalItems;
|
||||
}
|
||||
|
||||
return $last;
|
||||
}
|
||||
|
||||
public function setPreviousText($text)
|
||||
{
|
||||
$this->previousText = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setNextText($text)
|
||||
{
|
||||
$this->nextText = $text;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace JasonGrimes\Tests;
|
||||
|
||||
use JasonGrimes\Paginator;
|
||||
|
||||
class PaginatorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/** @var Paginator */
|
||||
protected $paginator;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$numItems = 100;
|
||||
$itemsPerPage = 10;
|
||||
$currentPage = 5;
|
||||
$urlPattern = '/example/page(:num)';
|
||||
|
||||
$this->paginator = new Paginator($numItems, $itemsPerPage, $currentPage, $urlPattern);
|
||||
}
|
||||
|
||||
public function testGetNextPage()
|
||||
{
|
||||
$this->paginator->setCurrentPage(1);
|
||||
$this->assertEquals(2, $this->paginator->getNextPage());
|
||||
|
||||
// If we're on the last page, getNextPage() returns null.
|
||||
$this->paginator->setCurrentPage($this->paginator->getNumPages());
|
||||
$this->assertNull($this->paginator->getNextPage());
|
||||
}
|
||||
|
||||
public function testGetPrevPage()
|
||||
{
|
||||
$this->paginator->setCurrentPage(2);
|
||||
$this->assertEquals(1, $this->paginator->getPrevPage());
|
||||
|
||||
// If we're on the first page, getPrevPage() returns null.
|
||||
$this->paginator->setCurrentPage(1);
|
||||
$this->assertNull($this->paginator->getPrevPage());
|
||||
}
|
||||
|
||||
public function testGetNextUrl()
|
||||
{
|
||||
$this->paginator->setCurrentPage(1);
|
||||
$this->paginator->setUrlPattern('/example/page(:num)');
|
||||
$this->assertEquals('/example/page2', $this->paginator->getNextUrl());
|
||||
|
||||
// Returns null if on the last page.
|
||||
$this->paginator->setCurrentPage($this->paginator->getNumPages());
|
||||
$this->assertNull($this->paginator->getNextUrl());
|
||||
}
|
||||
|
||||
public function testGetPrevUrl()
|
||||
{
|
||||
$this->paginator->setCurrentPage(2);
|
||||
$this->paginator->setUrlPattern('/example/page(:num)');
|
||||
$this->assertEquals('/example/page1', $this->paginator->getPrevUrl());
|
||||
|
||||
// Returns null if on the first page.
|
||||
$this->paginator->setCurrentPage(1);
|
||||
$this->assertNull($this->paginator->getPrevUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getTestData
|
||||
*/
|
||||
public function testGetPages($numPages, $currentPage, $maxPages, $expected)
|
||||
{
|
||||
$paginator = new Paginator($numPages, 1, $currentPage);
|
||||
$paginator->setMaxPagesToShow($maxPages);
|
||||
|
||||
$pages = $paginator->getPages();
|
||||
$pageNums = array_map(function($page) { return $page['num']; }, $pages);
|
||||
|
||||
$this->assertEquals($expected, $pageNums);
|
||||
}
|
||||
|
||||
public function getTestData()
|
||||
{
|
||||
return array(
|
||||
// num pages, current page, max pages to show, expected pagination
|
||||
array(13, 2, 5, array(1, 2, 3, 4, '...', 13)),
|
||||
array(13, 4, 5, array(1, '...', 3, 4, 5, '...', 13)),
|
||||
array(13, 5, 5, array(1, '...', 4, 5, 6, '...', 13)),
|
||||
array(13, 11, 5, array(1, '...', 10, 11, 12, 13)),
|
||||
array(13, 10, 5, array(1, '...', 9, 10, 11, '...', 13)),
|
||||
array(20, 1, 10, array(1, 2, 3, 4, 5, 6, 7, 8, 9, '...', 20)),
|
||||
array(20, 2, 10, array(1, 2, 3, 4, 5, 6, 7, 8, 9, '...', 20)),
|
||||
array(20, 20, 10, array(1, '...', 12, 13, 14, 15, 16, 17, 18, 19, 20)),
|
||||
array(20, 19, 10, array(1, '...', 12, 13, 14, 15, 16, 17, 18, 19, 20)),
|
||||
array(20, 10, 10, array(1, '...', 7, 8, 9, 10, 11, 12, 13, 14, '...', 20)),
|
||||
array(20, 9, 10, array(1, '...', 6, 7, 8, 9, 10, 11, 12, 13, '...', 20)),
|
||||
array(5, 3, 10, array(1, 2, 3, 4, 5)),
|
||||
array(1, 1, 10, array()), // No pagination if there's only one page.
|
||||
array(20, 5, 3, array(1, '...', 5, '...', 20)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getRangeData
|
||||
*/
|
||||
public function testGetItemRanges($numItems, $itemsPerPage, $currentPage, $expectedFirst, $expectedLast)
|
||||
{
|
||||
$paginator = new Paginator($numItems, $itemsPerPage, $currentPage);
|
||||
|
||||
$this->assertEquals($numItems, $paginator->getTotalItems());
|
||||
$this->assertEquals($expectedFirst, $paginator->getCurrentPageFirstItem());
|
||||
$this->assertEquals($expectedLast, $paginator->getCurrentPageLastItem());
|
||||
|
||||
}
|
||||
|
||||
public function getRangeData()
|
||||
{
|
||||
return array(
|
||||
// $numItems, $itemsPerPage, $currentPage, $expectedFirstItem, $expectedLastItem
|
||||
array(95, 10, 1, 1, 10),
|
||||
array(95, 10, 2, 11, 20),
|
||||
array(95, 10, 10, 91, 95),
|
||||
array(95, 10, 11, null, null), // If current page exceeds total items, first and last item are null.
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||