├── .gitignore ├── tests ├── bootstrap.php ├── Strana │ ├── MakeSureTest.php │ ├── EloquentTest.php │ ├── PixieTest.php │ ├── DoctrineDbalTest.php │ └── PaginatorTest.php └── TestCase.php ├── src └── Strana │ ├── Exceptions │ ├── Exception.php │ └── InvalidArgumentException.php │ ├── Interfaces │ └── CollectionAdapter.php │ ├── Views │ └── infiniteScroll.php │ ├── InfiniteScroll.php │ ├── Adapters │ ├── ArrayAdapter.php │ ├── EloquentAdapter.php │ ├── PixieAdapter.php │ └── DoctrineDbalAdapter.php │ ├── ViewLoader.php │ ├── ConfigHelper.php │ ├── RecordSet.php │ ├── LinkCreator.php │ └── Paginator.php ├── .travis.yml ├── phpunit.xml ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.phar 3 | composer.lock 4 | *~ -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Strana/Interfaces/CollectionAdapter.php: -------------------------------------------------------------------------------- 1 | 2 | !window.jQuery && document.write(' 4 | 5 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | tests/Strana/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "usmanhalalit/strana", 3 | "description": "Pagination library for PHP, framework agnostic, with built-in adapters for Doctrine, Eloquent, Pixie and PHP Array.", 4 | "homepage": "https://github.com/usmanhalalit/strana", 5 | "keywords": ["paginator", "pagination", "page"], 6 | "license": "MIT", 7 | "minimum-stability": "dev", 8 | "authors": 9 | [ 10 | { 11 | "name": "Muhammad Usman", 12 | "email": "hi@usman.it", 13 | "role": "Developer" 14 | } 15 | ], 16 | 17 | "require": { 18 | "php": ">=5.3.0" 19 | }, 20 | 21 | "require-dev": { 22 | "phpunit/phpunit": "3.7.*", 23 | "usmanhalalit/pixie": "1.*@dev", 24 | "illuminate/database": "4.1.*@dev", 25 | "doctrine/dbal": "2.3.4" 26 | }, 27 | 28 | "autoload": { 29 | "psr-0": { 30 | "Strana": "src/" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Muhammad Usman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/Strana/InfiniteScroll.php: -------------------------------------------------------------------------------- 1 | viewLoader = $viewLoader; 22 | $this->configHelper = $configHelper; 23 | } 24 | 25 | /** 26 | * @param array $config 27 | * @return mixed 28 | * 29 | * Prepare infinite scroll JavaScript 30 | */ 31 | public function getJs(Array $config = array()) 32 | { 33 | $default = array( 34 | 'container' => '.container', 35 | 'item' => '.item', 36 | 'pagination' => '.pagination', 37 | 'next' => '.next a', 38 | 'loader' => 'Loading ...', 39 | 'triggerPageThreshold' => 5, 40 | ); 41 | 42 | $data['config'] = array_merge($default, $config); 43 | 44 | return $this->viewLoader->load('infiniteScroll.php', $data); 45 | } 46 | } -------------------------------------------------------------------------------- /src/Strana/Adapters/ArrayAdapter.php: -------------------------------------------------------------------------------- 1 | records = $records; 31 | $this->configHelper = $configHelper; 32 | } 33 | 34 | /** 35 | * @return array 36 | */ 37 | public function slice() 38 | { 39 | $limit = $this->configHelper->getLimit(); 40 | $offset = $this->configHelper->getOffset(); 41 | return array_slice($this->records, $offset, $limit); 42 | } 43 | 44 | /** 45 | * @return int 46 | */ 47 | public function total() 48 | { 49 | return count($this->records); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Strana/ViewLoader.php: -------------------------------------------------------------------------------- 1 | records = $records; 32 | $this->configHelper = $configHelper; 33 | } 34 | 35 | /** 36 | * @return array|static[] 37 | */ 38 | public function slice() 39 | { 40 | $records = clone($this->records); 41 | $limit = $this->configHelper->getLimit(); 42 | $offset = $this->configHelper->getOffset(); 43 | return $records->limit($limit)->offset($offset)->get(); 44 | } 45 | 46 | /** 47 | * @return int 48 | */ 49 | public function total() 50 | { 51 | $records = clone($this->records); 52 | return $records->count(); 53 | } 54 | } -------------------------------------------------------------------------------- /tests/Strana/EloquentTest.php: -------------------------------------------------------------------------------- 1 | addConnection(array( 13 | 'driver' => 'sqlite', 14 | 'database' => ':memory:', 15 | )); 16 | $capsule->setAsGlobal(); 17 | $capsule->bootEloquent(); 18 | 19 | Capsule::statement("CREATE TABLE sample( 20 | t_key TEXT NOT NULL, 21 | t_value TEXT NOT NULL 22 | );"); 23 | 24 | for ($i = 1; $i <= 100; $i++) { 25 | $record = array( 26 | 't_key' => 'Key ' . $i, 27 | 't_value' => 'Value ' . $i, 28 | ); 29 | 30 | Capsule::table('sample')->insert($record); 31 | } 32 | } 33 | 34 | public function testPaginationWithLaravelAdapter() 35 | { 36 | $records = Capsule::table('sample'); 37 | $expected = Capsule::table('sample')->limit(20)->offset(60)->get(); 38 | $paginatorClass = new Paginator(); 39 | 40 | $paginator = $paginatorClass->page(4)->perPage(20)->make($records); 41 | 42 | $this->assertEquals(100, $paginator->total(), 'Failed asserting pagination total.'); 43 | $this->assertEquals($expected, $paginator->records(), 'Failed asserting pagination records.'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Strana/Adapters/PixieAdapter.php: -------------------------------------------------------------------------------- 1 | records = $records; 32 | $this->configHelper = $configHelper; 33 | } 34 | 35 | /** 36 | * @return null|\stdClass 37 | */ 38 | public function slice() 39 | { 40 | $records = clone($this->records); 41 | $limit = $this->configHelper->getLimit(); 42 | $offset = $this->configHelper->getOffset(); 43 | return $records->limit($limit)->offset($offset)->get(); 44 | } 45 | 46 | /** 47 | * @return int 48 | */ 49 | public function total() 50 | { 51 | $records = clone($this->records); 52 | return $records->count(); 53 | } 54 | } -------------------------------------------------------------------------------- /tests/Strana/PixieTest.php: -------------------------------------------------------------------------------- 1 | 'sqlite', 'database' => ':memory:')); 17 | $this->qb = new QueryBuilderHandler($connection); 18 | 19 | $this->qb->query("CREATE TABLE sample( 20 | t_key TEXT NOT NULL, 21 | t_value TEXT NOT NULL 22 | );"); 23 | 24 | for ($i = 1; $i <= 100; $i++) { 25 | $record = array( 26 | 't_key' => 'Key ' . $i, 27 | 't_value' => 'Value ' . $i, 28 | ); 29 | 30 | $this->qb->table('sample')->insert($record); 31 | } 32 | } 33 | 34 | public function testPaginatorGenerateWithPixieAdapter() 35 | { 36 | $records = $this->qb->table('sample')->select('t_value'); 37 | $expected = $this->qb->table('sample')->select('t_value')->limit(10)->offset(10)->get(); 38 | $paginatorClass = new Paginator(); 39 | 40 | $paginator = $paginatorClass->page(2)->perPage(10)->make($records); 41 | 42 | $this->assertEquals(100, $paginator->total(), 'Failed asserting pagination total.'); 43 | $this->assertEquals($expected, $paginator->records(), 'Failed asserting pagination records.'); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Strana/Adapters/DoctrineDbalAdapter.php: -------------------------------------------------------------------------------- 1 | records = $records; 32 | $this->configHelper = $configHelper; 33 | } 34 | 35 | /** 36 | * @return mixed|array 37 | */ 38 | public function slice() 39 | { 40 | $records = clone($this->records); 41 | $limit = $this->configHelper->getLimit(); 42 | $offset = $this->configHelper->getOffset(); 43 | return $records->setMaxResults($limit) 44 | ->setFirstResult($offset) 45 | ->execute() 46 | ->fetchAll(); 47 | } 48 | 49 | /** 50 | * @return mixed 51 | */ 52 | public function total() 53 | { 54 | $records = clone($this->records); 55 | $records->select('count(*) as cnt'); 56 | $count = $records->execute()->fetchColumn(); 57 | return $count; 58 | } 59 | } -------------------------------------------------------------------------------- /tests/Strana/DoctrineDbalTest.php: -------------------------------------------------------------------------------- 1 | ':memory:', 21 | 'driver' => 'pdo_sqlite', 22 | ); 23 | $conn = DriverManager::getConnection($connectionParams, $config); 24 | $this->qb = $conn->createQueryBuilder(); 25 | 26 | $sql = "CREATE TABLE sample( 27 | t_key TEXT NOT NULL, 28 | t_value TEXT NOT NULL 29 | );"; 30 | $conn->query($sql); 31 | 32 | for ($i = 1; $i <= 90; $i++) { 33 | $conn->query("INSERT INTO SAMPLE VALUES ('Key$i', 'Value$i')"); 34 | } 35 | } 36 | 37 | public function testPaginationWithDoctrineDbalAdapter() 38 | { 39 | $cqb = clone($this->qb); 40 | $records = $this->qb->select('*')->from('sample', 'sample'); 41 | 42 | $cqb->select('*')->from('sample', 'sample')->setMaxResults(20)->setFirstResult(60); 43 | $expected = $cqb->execute()->fetchAll(); 44 | /*var_dump($this->qb->execute()->fetchAll()); 45 | exit;*/ 46 | $paginatorClass = new Paginator(); 47 | $paginator = $paginatorClass->page(4)->perPage(20)->make($records); 48 | 49 | $this->assertEquals(90, $paginator->total(), 'Failed asserting pagination total.'); 50 | $this->assertEquals($expected, $paginator->records(), 'Failed asserting pagination records.'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Strana/ConfigHelper.php: -------------------------------------------------------------------------------- 1 | config = $config; 15 | $this->setDefaults(); 16 | } 17 | 18 | /** 19 | * Set default config values 20 | */ 21 | protected function setDefaults() 22 | { 23 | $get = $_GET; 24 | $page = isset($get['page']) ? (int) $get['page'] : 1; 25 | $defaults = array( 26 | 'perPage' => 20, 27 | 'page' => $page, 28 | 'maximumPages' => 5, 29 | 'infiniteScroll' => false, 30 | ); 31 | 32 | $this->config = array_merge($defaults, $this->config); 33 | } 34 | 35 | /** 36 | * @return mixed 37 | */ 38 | public function getCurrentPage() 39 | { 40 | return $this->config['page']; 41 | } 42 | 43 | /** 44 | * @return mixed 45 | */ 46 | public function getOffset() 47 | { 48 | return $this->config['perPage'] * ($this->config['page'] - 1); 49 | } 50 | 51 | /** 52 | * @return mixed 53 | */ 54 | public function getLimit() 55 | { 56 | return $this->config['perPage']; 57 | } 58 | 59 | /** 60 | * @return mixed 61 | */ 62 | public function getMaximumPages() 63 | { 64 | return $this->config['maximumPages']; 65 | } 66 | 67 | /** 68 | * @return mixed 69 | */ 70 | public function getInfiniteScroll() 71 | { 72 | return $this->config['infiniteScroll']; 73 | } 74 | 75 | /** 76 | * @param $totalRecords 77 | * @return float 78 | */ 79 | public function getTotalPages($totalRecords) 80 | { 81 | $pages = $totalRecords / $this->getLimit(); 82 | // If we have decimal value like 2.2 then we need 3 pages, ceil it. 83 | $pages = ceil($pages); 84 | return $pages; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /src/Strana/RecordSet.php: -------------------------------------------------------------------------------- 1 | records = $records; 34 | $this->total = $total; 35 | $this->links = $links; 36 | } 37 | 38 | /** 39 | * @return array 40 | */ 41 | public function records() 42 | { 43 | return $this->records; 44 | } 45 | 46 | /** 47 | * @return int 48 | */ 49 | public function total() 50 | { 51 | return $this->total; 52 | } 53 | 54 | /** 55 | * @return mixed|void 56 | */ 57 | public function rewind() 58 | { 59 | return reset($this->records); 60 | } 61 | 62 | /** 63 | * @return mixed 64 | */ 65 | public function current() 66 | { 67 | return current($this->records); 68 | } 69 | 70 | /** 71 | * @return mixed 72 | */ 73 | public function key() 74 | { 75 | return key($this->records); 76 | } 77 | 78 | /** 79 | * @return mixed|void 80 | */ 81 | public function next() 82 | { 83 | return next($this->records); 84 | } 85 | 86 | /** 87 | * @return bool 88 | */ 89 | public function valid() 90 | { 91 | return key($this->records) !== null; 92 | } 93 | 94 | /** 95 | * @param $links 96 | */ 97 | public function setLinks($links) 98 | { 99 | $this->links = $links; 100 | } 101 | 102 | /** 103 | * @return null|string 104 | */ 105 | public function links() 106 | { 107 | return $this->links; 108 | } 109 | 110 | 111 | /** 112 | * @return null|string 113 | */ 114 | public function __toString() 115 | { 116 | return $this->links(); 117 | } 118 | } -------------------------------------------------------------------------------- /tests/Strana/PaginatorTest.php: -------------------------------------------------------------------------------- 1 | make($records, 'Array'); 15 | 16 | $expected = array(); 17 | for ($i = 1; $i <= 20; $i++) { 18 | $expected['Key ' . $i] = 'Value ' . $i; 19 | } 20 | 21 | $this->assertSame($expected, $paginator->records(), 'Failed asserting pagination records.'); 22 | $this->assertEquals(100, $paginator->total(), 'Failed asserting pagination total.'); 23 | } 24 | 25 | public function testPaginatorGenerateWithArrayAdapter() 26 | { 27 | $paginatorClass = new Paginator(); 28 | $records = array(); 29 | for ($i = 1; $i <= 100; $i++) { 30 | $records['Key ' . $i] = 'Value ' . $i; 31 | } 32 | 33 | $config = array( 34 | 'perPage' => 10, 35 | 'page' => 2, 36 | ); 37 | $iasConfig = array('loaderDelay' => 800); 38 | $paginator = $paginatorClass->infiniteScroll($iasConfig)->make($records, 'Array', $config); 39 | 40 | $expected = array(); 41 | for ($i = 11; $i <= 20; $i++) { 42 | $expected['Key ' . $i] = 'Value ' . $i; 43 | } 44 | 45 | $this->assertSame($expected, $paginator->records(), 'Failed asserting pagination records.'); 46 | $this->assertEquals(100, $paginator->total(), 'Failed asserting pagination total.'); 47 | } 48 | 49 | public function testHTMLOutput() 50 | { 51 | $paginatorClass = new Paginator(); 52 | $records = array(); 53 | for ($i = 1; $i <= 100; $i++) { 54 | $records['Key ' . $i] = 'Value ' . $i; 55 | } 56 | 57 | $paginator = $paginatorClass->page(4)->perPage(10)->make($records, 'Array'); 58 | 59 | // Cover foreach, iterator 60 | foreach($paginator as $item) { 61 | 62 | } 63 | 64 | $expected = ''; 65 | $this->assertEquals($expected, (string)$paginator); 66 | } 67 | 68 | /** 69 | * @expectedException Strana\Exceptions\InvalidArgumentException 70 | */ 71 | public function testAdapterNotFoundException() 72 | { 73 | $paginatorClass = new Paginator(); 74 | $paginatorClass->make(array(), 'Foo'); 75 | } 76 | 77 | /** 78 | * @expectedException Strana\Exceptions\InvalidArgumentException 79 | */ 80 | public function testWithInvalidAdapter() 81 | { 82 | $paginatorClass = new Paginator(); 83 | $paginatorClass->make(array(), new \stdClass()); 84 | } 85 | } -------------------------------------------------------------------------------- /src/Strana/LinkCreator.php: -------------------------------------------------------------------------------- 1 | configHelper = $configHelper; 22 | $this->infiniteScroll = $infiniteScroll; 23 | } 24 | 25 | /** 26 | * @param $totalRecords 27 | * @return string 28 | */ 29 | public function createLinks($totalRecords) 30 | { 31 | $currentPage = $this->configHelper->getCurrentPage(); 32 | $totalPages = $this->configHelper->getTotalPages($totalRecords); 33 | $pages = $this->getPages($totalRecords, $this->configHelper->getLimit(), $currentPage, $this->configHelper->getMaximumPages()); 34 | 35 | $prevLiClass = 'prev'; 36 | $prevLinkHref = 'javascript:void(0)'; 37 | if ($currentPage == 1) { 38 | $prevLiClass = 'disabled'; 39 | } else { 40 | $prevLinkHref = $this->buildQueryString($currentPage - 1); 41 | } 42 | 43 | $nextLiClass = 'next'; 44 | $nextLinkHref = 'javascript:void(0)'; 45 | if ($currentPage == $totalPages) { 46 | $nextLiClass = 'disabled'; 47 | } else { 48 | $nextLinkHref = $this->buildQueryString($currentPage + 1); 49 | } 50 | 51 | $output = ''; 59 | 60 | return $this->addInfiniteScroll($output); 61 | } 62 | 63 | protected function buildQueryString($page) 64 | { 65 | $get = $_GET; 66 | $get['page'] = $page; 67 | $queryString = http_build_query($get); 68 | return $queryString = '?' . $queryString; 69 | } 70 | 71 | /** 72 | * @param $output 73 | * @return string 74 | */ 75 | protected function addInfiniteScroll($output) 76 | { 77 | if (($config = $this->configHelper->getInfiniteScroll()) !== false) { 78 | $output = $output . $this->infiniteScroll->getJs($config); 79 | } 80 | 81 | return $output; 82 | } 83 | 84 | /** 85 | * @param $total 86 | * @param null $limit 87 | * @param null $current 88 | * @param null $adjacents 89 | * @return array 90 | * 91 | * Credit: http://stackoverflow.com/a/7562895/656489 92 | */ 93 | protected function getPages($total, $limit = null, $current = null, $adjacents = null) 94 | { 95 | $result = array(); 96 | 97 | if (isset($total, $limit) === true) 98 | { 99 | $result = range(1, ceil($total / $limit)); 100 | 101 | if (isset($current, $adjacents) === true) 102 | { 103 | if (($adjacents = floor($adjacents / 2) * 2 + 1) >= 1) 104 | { 105 | $result = array_slice($result, max(0, min(count($result) - $adjacents, intval($current) - ceil($adjacents / 2))), $adjacents); 106 | } 107 | } 108 | } 109 | 110 | return $result; 111 | } 112 | } -------------------------------------------------------------------------------- /src/Strana/Paginator.php: -------------------------------------------------------------------------------- 1 | getConfig(), $config); 33 | $this->setConfig($config); 34 | $configHelper = new ConfigHelper($this->getConfig()); 35 | 36 | if ($adapter) { 37 | $this->setAdapter($adapter); 38 | } 39 | 40 | $recordSet = $this->generate($records,$configHelper); 41 | $infiniteScroll = new InfiniteScroll(new ViewLoader(), $configHelper); 42 | 43 | $linkCreator = new LinkCreator($configHelper, $infiniteScroll); 44 | $links = $linkCreator->createLinks($recordSet->total()); 45 | $recordSet->setLinks($links); 46 | return $recordSet; 47 | } 48 | 49 | /** 50 | * @param $currentPage 51 | * @return $this 52 | * 53 | * Set current page 54 | */ 55 | public function page($currentPage) 56 | { 57 | $this->config['page'] = $currentPage; 58 | return $this; 59 | } 60 | 61 | /** 62 | * @param $perPage 63 | * @return $this 64 | * 65 | * Set items to be shown per page 66 | */ 67 | public function perPage($perPage) 68 | { 69 | $this->config['perPage'] = $perPage; 70 | return $this; 71 | } 72 | 73 | /** 74 | * @param array $config 75 | * @return $this 76 | * 77 | * Enable infinite scroll and set config 78 | */ 79 | public function infiniteScroll(Array $config = array()) 80 | { 81 | $this->config['infiniteScroll'] = $config; 82 | return $this; 83 | } 84 | 85 | /** 86 | * @param array $config 87 | */ 88 | public function setConfig(Array $config) 89 | { 90 | $this->config = $config; 91 | } 92 | 93 | /** 94 | * @return array 95 | */ 96 | public function getConfig() 97 | { 98 | return $this->config; 99 | } 100 | 101 | /** 102 | * @param $adapter 103 | * @return $this 104 | */ 105 | public function setAdapter($adapter) 106 | { 107 | $this->adapter = $adapter; 108 | return $this; 109 | } 110 | 111 | /** 112 | * @return mixed 113 | */ 114 | public function getAdapter() 115 | { 116 | return $this->adapter; 117 | } 118 | 119 | /** 120 | * @param $records 121 | * @param ConfigHelper $configHelper 122 | * @return RecordSet 123 | */ 124 | protected function generate($records, ConfigHelper $configHelper) 125 | { 126 | $adapterInstance = $this->makeAdapterInstance($this->getAdapter(), $records, $configHelper); 127 | 128 | $total = $adapterInstance->total(); 129 | $slicedRecords = $adapterInstance->slice(); 130 | $recordSet = new RecordSet($slicedRecords, $total); 131 | 132 | return $recordSet; 133 | } 134 | 135 | protected function makeAdapterInstance($adapter, $records, $configHelper) 136 | { 137 | if (is_object($adapter)) { 138 | // User defined custom adapter 139 | if (!$adapter instanceof CollectionAdapter) { 140 | throw new InvalidArgumentException('Adapter must implement Strana\Interfaces\CollectionAdapter.'); 141 | } 142 | $adapterInstance = $adapter; 143 | } else { 144 | if (!$adapter || !is_string($adapter)) { 145 | // Auto detect 146 | $adapter = $this->detectAdapter($records); 147 | } 148 | 149 | $adapter = '\\Strana\\Adapters\\' . $adapter . 'Adapter'; 150 | if (!class_exists($adapter)) { 151 | throw new InvalidArgumentException('Adapter not found ' . $adapter . '.' ); 152 | } 153 | 154 | $adapterInstance = new $adapter($records, $configHelper); 155 | } 156 | 157 | return $adapterInstance; 158 | } 159 | 160 | /** 161 | * @param $records 162 | * @return string 163 | */ 164 | protected function detectAdapter($records) 165 | { 166 | if (is_array($records)) { 167 | return 'Array'; 168 | } elseif($records instanceof PixieQB) { 169 | return 'Pixie'; 170 | } elseif($records instanceof DoctrineDbalQB) { 171 | return 'DoctrineDbal'; 172 | } elseif($records instanceof EloquentQB) { 173 | return 'Eloquent'; 174 | } else { 175 | return 'Undefined'; 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Strana 2 | ### Smart Pagination Library for PHP 3 | 4 | [![Build Status](https://travis-ci.org/usmanhalalit/strana.png?branch=master)](https://travis-ci.org/usmanhalalit/strana) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/49091284-bd4e-455d-b821-ad6b33d25d37/small.png)](https://insight.sensiolabs.com/projects/49091284-bd4e-455d-b821-ad6b33d25d37 "This project passes all Insight checks successfully. This is very rare, and is worthy of the Platinum medal. Congratulations to all the contributors to this project for such a high quality.") [![Scrutinizer Code Quality Score](https://scrutinizer-ci.com/g/usmanhalalit/strana/badges/quality-score.png?s=41d13b5c7a983d1ed3637998d599e8e020e87538)](https://scrutinizer-ci.com/g/usmanhalalit/strana/) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/usmanhalalit/strana/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Dependency Status](http://www.versioneye.com/php/usmanhalalit:strana/dev-master/badge.png)](http://www.versioneye.com/php/usmanhalalit:strana/dev-master) 5 | ___ 6 | 7 | A framework agnostic, smart pagination library for PHP. Just a few lines of code and fully functional pagination is ready. 8 | 9 | Paginate your records with Strana. Strana will slice(limit and offset) these records, generate pagination links for you and reads page number from them, all automatically. 10 | 11 | #### Features: 12 | 13 | - Built-in adapters for [Doctrine](http://www.doctrine-project.org/projects/dbal.html), [Eloquent (Laravel)](https://github.com/illuminate/database), [Pixie](https://github.com/usmanhalalit/pixie), PHP Array and you can do it manually. 14 | - Readable syntax 15 | - Add Infinite Scroll with one line 16 | - It automatically detects which DBAL you are using. 17 | - Styles automatically with Twitter Bootstrap, Zurb Foundation and most of other CSS frameworks. 18 | 19 | 20 | #### Screenshot: 21 | 22 | ![Screenshot](https://dl.dropboxusercontent.com/u/15827461/strana-sample.png) 23 | 24 | ## Example 25 | Basically Strana makes it very easy, like the code below: 26 | ```PHP 27 | $paginator = $strana->perPage(10)->make($records); 28 | ``` 29 | **That's basically it.** 30 | 31 | ### Full Usage Example 32 | ```PHP 33 | // Make sure you have Composer's autoload file included 34 | require 'vendor/autoload.php'; 35 | 36 | $strana = new \Strana\Paginator(); 37 | $records = array(1, 2, 3, .... 100); 38 | $paginator = $strana->perPage(10)->make($records); 39 | 40 | // Loop paginated items 41 | foreach ($paginator as $item) { 42 | echo $item['field_name'] . '
'; 43 | } 44 | 45 | // Print pagination links 46 | echo '

' . $paginator; 47 | ``` 48 | 49 | 50 | There are some advanced options which are documented below. Sold? Lets install. 51 | 52 | ## Installation 53 | 54 | Strana uses [Composer](http://getcomposer.org/doc/00-intro.md#installation-nix) to make things easy, its a requirement. 55 | 56 | Learn to use composer and add this to require section (in your composer.json): 57 | 58 | "usmanhalalit/strana": "1.*@dev" 59 | 60 | And run: 61 | 62 | composer update 63 | 64 | Library on [Packagist](https://packagist.org/packages/usmanhalalit/strana). 65 | 66 | 67 | 68 | ## Full Walkthrough 69 | 70 | Strana has built in adapters for Doctrine Dbal, Laravel Eloquent, Pixie and PHP native arrays. If you want to paginate from any of these then you're in luck. More adapters will come according to feedback. 71 | 72 | ### Step 1: 73 | Prepare your database or array records: 74 | 75 | **Doctrine Dbal Example:** 76 | 77 | ```PHP 78 | $records = $qb->select('*')->from('sample', 'sample'); 79 | ``` 80 | **Laravel Eloquent (or Query Builder) Example:** 81 | 82 | ```PHP 83 | $records = Capsule::select('*')->from('sample'); 84 | ``` 85 | **Pixie Example:** 86 | 87 | ```PHP 88 | $records = QB::select('*')->from('sample'); 89 | ``` 90 | **Array Example:** 91 | 92 | ```PHP 93 | $records = array(1, 2, 3, 4, 5); 94 | ``` 95 | 96 | ### Step 2: 97 | Paginate your records with Strana. Strana will slice(limit and offset) these records, generate pagination links for you and reads page number from them, all automatically. 98 | 99 | ```PHP 100 | $strana = new \Strana\Paginator(); 101 | $paginator = $strana->perPage(10)->make($records); 102 | ``` 103 | 104 | ### Step 3: 105 | Loop paginated records to display: 106 | 107 | ```PHP 108 | foreach ($paginator as $item) { 109 | echo $item['field'] . '
'; 110 | } 111 | ``` 112 | 113 | ### Step 4: 114 | Print your pagination links: 115 | ```PHP 116 | echo $paginator; 117 | ``` 118 | 119 | It will produce something like this: 120 | 121 | ![Screenshot](https://dl.dropboxusercontent.com/u/15827461/strana-sample.png) 122 | 123 | 124 | ## Infinite Scroll 125 | Strana comes with out of the box Infinite Scrolling, enable it with just one method. 126 | 127 | ```PHP 128 | $strana->infiniteScroll()->perPage(10)->make($records); 129 | ``` 130 | And then wrap all your pagination items and pagination links with certain CSS classes, like the example below. 131 | 132 | #### Example 133 | 134 | ```PHP 135 | echo '
'; 136 | foreach ($paginator as $item) { 137 | print('
' . $item['t_value'] . '
'); 138 | } 139 | 140 | 141 | echo '

' . $paginator; 142 | echo '
'; 143 | ``` 144 | 145 | **That's it**, you're done with infinite scrolling. 146 | ___ 147 | 148 | Strana uses the awesome [Infinite Ajax Scroll](https://github.com/webcreate/Infinite-Ajax-Scroll) jQuery plugin. All config options supported by this plugin can be passed with Strana. 149 | 150 | ```PHP 151 | $iasConfig = array( 152 | 'loaderDelay' => 600, 153 | 'loader' => '', 154 | ); 155 | 156 | $strana->infiniteScroll($iasConfig)->perPage(10)->make($records); 157 | ``` 158 | 159 | Cool, yeah? 160 | 161 | ## Usage API 162 | #### perPage($perPage) 163 | Number of items on per page, default 10. 164 | 165 | 166 | #### page($page) 167 | Which page to show, default is read from query string else 1. 168 | 169 | 170 | #### infiniteScroll(Array $config = array()) 171 | Enable Infinite Scrolling using Ajax. Options can be passed using `$config`. 172 | 173 | #### make($records, $adapter = null, $config = array()) 174 | Make and return paginator object. 175 | 176 | `$records` = records to be paginated. 177 | 178 | `$adapter` = which adapter you want to use as string, `DoctrineDbal`, `Eloquent`, `Pixie`, `Array` and so. If omitted or a falsy value is given then Strana is smart enough to detect the adapter itself. 179 | If you want to use your own(custom) adapter then pass the object/instance here. Your custom adapter must implement ` Strana\Interfaces\CollectionAdapter` interface. 180 | 181 | `$config` = all Strana config can be passed here too, as an array. like `$config = array('maximumPages' => 7)` 182 | 183 | ## The Recordset Object 184 | 185 | `make()` method returns an instance of `Strana\RecordSet` class. Its a **polymorphic** object, for example: 186 | ```PHP 187 | $paginator = $strana->make($records); 188 | ``` 189 | Here, if you loop `$paginator` with `foreach` then it will work like an array and iterate through paginated items, if you `echo` `$paginator` it will work like a string and print the pagination links. And of course you can use like a class object. 190 | `$paginator->records()` will return paginated records. 191 | `$paginator->total()` will return total records count. 192 | `$paginator->links()` will return pagination links. 193 | 194 | 195 | ## Developing Your Own Adapter 196 | 197 | If you are not using the database tool whose adapter ships with Strana then you can build your own adapter with ease. Create your class which must implement `Strana\Interfaces\CollectionAdapter`. 198 | 199 | Example adapter, please read comments to understand. 200 | 201 | 202 | ```PHP 203 | records = $records; 224 | $this->configHelper = $configHelper; 225 | } 226 | 227 | /** 228 | * This method should limit and offset your records and return. 229 | */ 230 | public function slice() 231 | { 232 | // Here you will get the database object passed to Strana. 233 | // Clone it. 234 | $records = clone($this->records); 235 | 236 | // Get the limit number from Strana config 237 | $limit = $this->configHelper->getLimit(); 238 | 239 | // Get the offset number from Strana config 240 | $offset = $this->configHelper->getOffset(); 241 | 242 | // Limit your records 243 | $records->limit($limit); 244 | // Offset your records 245 | $records->offset($offset); 246 | 247 | // Return your sliced records 248 | return $records->get(); 249 | } 250 | 251 | /** 252 | * This method should return total count of all of your records. 253 | */ 254 | public function total() 255 | { 256 | // Here you will get the database object passed to Strana. 257 | // Clone it. 258 | $records = clone($this->records); 259 | 260 | // Return your total records count, unsliced. 261 | return $records->count(); 262 | } 263 | } 264 | ``` 265 | 266 | Please also look at the `ArrayAdapter` to get more idea. 267 | 268 | #### Using Your Adapter 269 | 270 | 271 | ```PHP 272 | $strana = new \Strana\Paginator(); 273 | $configHelper = new \Strana\ConfigHelper($strana->getConfig()); 274 | $adapter = new CustomAdapter($yourRecords, $configHelper); 275 | $paginator = $paginatorClass->make($yourRecords, $adapter); 276 | ``` 277 | 278 | ___ 279 | © 2013 [Muhammad Usman](http://usman.it/). Licensed under MIT license. 280 | --------------------------------------------------------------------------------