Overview

Packages

  • Pinoco
    • PAL

Classes

  • Pinoco
  • Pinoco_Delegate
  • Pinoco_DynamicVars
  • Pinoco_HttpRequestVars
  • Pinoco_List
  • Pinoco_MIMEType
  • Pinoco_NativeRenderer
  • Pinoco_NothingVars
  • Pinoco_NullRenderer
  • Pinoco_OptionalParam
  • Pinoco_Pagination
  • Pinoco_PDOStatementWrapper
  • Pinoco_PDOWrapper
  • Pinoco_Renderer
  • Pinoco_Router
  • Pinoco_TALRenderer
  • Pinoco_TestEnvironment
  • Pinoco_Validator
  • Pinoco_ValidatorContext
  • Pinoco_Vars

Interfaces

  • Pinoco_ArrayConvertible

Functions

  • __pinoco_autoload_impl
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Pinoco: makes existing static web site dynamic transparently.
  4:  * Copyright 2010-2012, Hisateru Tanaka <tanakahisateru@gmail.com>
  5:  *
  6:  * Licensed under The MIT License
  7:  * Redistributions of files must retain the above copyright notice.
  8:  *
  9:  * PHP Version 5
 10:  *
 11:  * @author     Hisateru Tanaka <tanakahisateru@gmail.com>
 12:  * @copyright  Copyright 2010-2012, Hisateru Tanaka <tanakahisateru@gmail.com>
 13:  * @license    MIT License (http://www.opensource.org/licenses/mit-license.php)
 14:  * @package    Pinoco
 15:  */
 16: 
 17: /**
 18:  * Pagination object designed for PHPTAL.
 19:  * This object is independent from RDBMS. You can use it with any data source.
 20:  *
 21:  * <code>
 22:  * $pagination = new Pinoco_Pagination(
 23:  *     // How many elements?
 24:  *     function($pagination) {
 25:  *         return $pagination->db->prepare(
 26:  *             "SELECT count(id) as c FROM ..."
 27:  *         )->query()->fetchOne()->c;
 28:  *     },
 29:  *     // What to be shown?
 30:  *     function($pagination, $offset, $limit) {
 31:  *         return $pagination->db->prepare(
 32:  *             "SELECT * FROM ... LIMIT $offset, $limit"
 33:  *         )->query()->fetchAll();
 34:  *     },
 35:  *     // How the page number is formatted in navigation?
 36:  *     function($pagination, $page) {
 37:  *         return 'list' . ($page > 1 ? '?page=' . $page : '');
 38:  *     },
 39:  *     array(
 40:  *         'elementsPerPage' => 20,
 41:  *         'db' => $db, // you can pass any custom property to pagination.
 42:  *     )
 43:  * );
 44:  * $pagination->page = 1;
 45:  * if (!$pagination->isValidPage) { $this->notfound(); }
 46:  * </code>
 47:  *
 48:  * PHPTAL example
 49:  * <code>
 50:  * <tal:block tal:repeat="element pagination/data">
 51:  *    ${element/prop}
 52:  * </tal:block>
 53:  * <div class="pagination"
 54:  *     tal:define="prev pagination/prev; pages pagination/pages; next pagination/next">
 55:  *     <!--! prev button -->
 56:  *     <a href="" tal:condition="prev/enabled"
 57:  *        tal:attributes="href url:${prev/href}">PREV</a>
 58:  *     <span class="disabled" tal:condition="not:prev/enabled">PREV</span>
 59:  *     <!--! page link buttons -->
 60:  *     <tal:block tal:repeat="page pages">
 61:  *         <tal:block tal:condition="not:page/padding">
 62:  *             <a href="" tal:condition="not:page/current"
 63:  *                tal:attributes="href url:${page/href}"
 64:  *                tal:content="page/number">1</a>
 65:  *             <span class="current" tal:condition="page/current"
 66:  *                tal:content="page/number">1</span>
 67:  *         </tal:block>
 68:  *         <span tal:condition="page/padding">...</span>
 69:  *     </tal:block>
 70:  *     <!--! next button -->
 71:  *     <a href="" tal:condition="next/enabled"
 72:  *        tal:attributes="href url:${next/href}">NEXT</a>
 73:  *     <span class="disabled" tal:condition="not:next/enabled">NEXT</span>
 74:  * </div>
 75:  * </code>
 76:  *
 77:  * @package Pinoco
 78:  * @property integer $page The page number that starts with 1. (not 0!)
 79:  * @property integer $elementsPerPage Amount of data shown in single page.
 80:  * @property integer $pagesAfterFirst How many buttons after the first page. (-1: hides first page)
 81:  * @property integer $pagesAroundCurrent How many buttons around current page. (-1: expand all pages)
 82:  * @property integer $pagesBeforeLast How many buttons before the last page. (-1: hides last page)
 83:  * @property-read integer $totalCount Total number of elements.
 84:  * @property-read integer $totalPages Total number of pages.
 85:  * @property-read mixed $data Elements in paginated range.
 86:  * @property-read Pinoco_List $pages Navigation information of each page buttons.
 87:  * @property-read Pinoco_Vars $prev Navigation information of the prev button.
 88:  * @property-read Pinoco_Vars $next Navigation information of the prev button.
 89:  */
 90: class Pinoco_Pagination extends Pinoco_DynamicVars
 91: {
 92:     private $totalCountCallable;
 93:     private $dataFetchCallable;
 94:     private $urlFormatCallable;
 95:     private $_currentPage;
 96:     private $_elementsPerPage;
 97:     private $_pagesAfterFirst;
 98:     private $_pagesAroundCurrent;
 99:     private $_pagesBeforeLast;
100: 
101:     private $_totalCount;
102:     private $_data;
103: 
104:     /**
105:      * Creates pagination object from user codes.
106:      *
107:      * @param callback $totalCountCallable
108:      * @param callback $dataFetchCallable
109:      * @param callback $urlFormatCallable
110:      * @param array $options
111:      */
112:     public function __construct($totalCountCallable, $dataFetchCallable,
113:         $urlFormatCallable, $options=array())
114:     {
115:         $this->totalCountCallable = $totalCountCallable;;
116:         $this->dataFetchCallable = $dataFetchCallable;
117:         $this->urlFormatCallable = $urlFormatCallable;
118:         $this->_currentPage = 1;
119:         $this->_elementsPerPage = 10;
120:         $this->_pagesAfterFirst = 0;
121:         $this->_pagesAroundCurrent = 1;
122:         $this->_pagesBeforeLast = 0;
123:         $this->_totalCount = null;
124:         $this->_data = null;
125:         foreach ($options as $name=>$value) {
126:             $this->set($name, $value);
127:         }
128:     }
129: 
130:     public function get_page()
131:     {
132:         return $this->_currentPage;
133:     }
134:     public function set_page($value)
135:     {
136:         if ($value < 1) {
137:             throw new InvalidArgumentException('Invalid number of page:' . $value);
138:         }
139:         if ($value != $this->_currentPage) {
140:             $this->_data = null;
141:         }
142:         $this->_currentPage = $value;
143:     }
144: 
145:     public function get_isValidPage() {
146:         return $this->page <= $this->totalPages;
147:     }
148: 
149:     public function get_elementsPerPage()
150:     {
151:         return $this->_elementsPerPage;
152:     }
153:     public function set_elementsPerPage($value)
154:     {
155:         if ($value < 1) {
156:             throw new InvalidArgumentException('Invalid number of elements:' . $value);
157:         }
158:         $this->_elementsPerPage = $value;
159:         $this->_data = null;
160:     }
161: 
162:     public function get_pagesAfterFirst()
163:     {
164:         return $this->_pagesAfterFirst;
165:     }
166:     public function set_pagesAfterFirst($value)
167:     {
168:         if ($value < -1) {
169:             throw new InvalidArgumentException('Invalid number of links:' . $value);
170:         }
171:         $this->_pagesAfterFirst = $value;
172:     }
173: 
174:     public function get_pagesAroundCurrent()
175:     {
176:         return $this->_pagesAroundCurrent;
177:     }
178:     public function set_pagesAroundCurrent($value)
179:     {
180:         if ($value < -1) {
181:             throw new InvalidArgumentException('Invalid number of links:' . $value);
182:         }
183:         $this->_pagesAroundCurrent = $value;
184:     }
185: 
186:     public function get_pagesBeforeLast()
187:     {
188:         return $this->_pagesBeforeLast;
189:     }
190:     public function set_pagesBeforeLast($value)
191:     {
192:         if ($value < -1) {
193:             throw new InvalidArgumentException('Invalid number of links:' . $value);
194:         }
195:         $this->_pagesBeforeLast = $value;
196:     }
197: 
198:     /**
199:      * Total number of elements.
200:      *
201:      * @return integer
202:      */
203:     public function get_totalCount()
204:     {
205:         if (is_null($this->_totalCount)) {
206:             $this->_totalCount = call_user_func($this->totalCountCallable, $this);
207:         }
208:         return $this->_totalCount;
209:     }
210: 
211:     /**
212:      * Elements in paginated range.
213:      * The fetched result is cached before changing current page.
214:      *
215:      * @return mixed
216:      */
217:     public function get_data()
218:     {
219:         if (is_null($this->_data)) {
220:             $this->_data = call_user_func(
221:                 $this->dataFetchCallable,
222:                 $this,
223:                 ($this->page - 1) * $this->elementsPerPage, // offset
224:                 $this->elementsPerPage // limit
225:             );
226:         }
227:         return $this->_data;
228:     }
229: 
230:     /**
231:      * Force to clear cached data.
232:      *
233:      * @return void
234:      */
235:     public function reset()
236:     {
237:         $this->_data = null;
238:         $this->_totalCount = null;
239:     }
240: 
241:     /**
242:      * Total number of pages.
243:      *
244:      * @return integer
245:      */
246:     public function get_totalPages()
247:     {
248:         return max(1, intval(($this->totalCount - 1) / $this->elementsPerPage) + 1);
249:     }
250: 
251:     /**
252:      * Navigation information of each page buttons.
253:      *
254:      * @return Pinoco_List
255:      */
256:     public function get_pages()
257:     {
258:         $pages = Pinoco::newList();
259:         $leftpad = false;
260:         $rightpad = false;
261:         for ($i = 1; $i <= $this->totalPages; $i++) {
262:             if ($this->_pagesAroundCurrent >= 0) {
263:                 $skipped = false;
264:                 if (!$leftpad && $i > 1 + $this->_pagesAfterFirst) {
265:                     $leftpad = true;
266:                     if (abs($i - $this->_currentPage) > $this->_pagesAroundCurrent) {
267:                         $skipped = true;
268:                         $skipto = max($i+1, $this->_currentPage - $this->_pagesAroundCurrent);
269:                     }
270:                 }
271:                 if ($leftpad && !$rightpad && $i > $this->_currentPage + $this->_pagesAroundCurrent) {
272:                     $rightpad = true;
273:                     if ($i < $this->totalPages - $this->_pagesBeforeLast) {
274:                         $skipped = true;
275:                         $skipto = max($i, $this->totalPages - $this->_pagesBeforeLast);
276:                     }
277:                 }
278:                 if ($skipped && isset($skipto)) {
279:                     $pages->push(Pinoco::newVars(array('padding' => true)));
280:                     $i = $skipto - 1;
281:                     continue;
282:                 }
283:             }
284:             $pages->push(Pinoco::newVars(array(
285:                 'padding' => false,
286:                 'number'  => $i,
287:                 'href'    => call_user_func($this->urlFormatCallable, $this, $i),
288:                 'current' => $i == $this->_currentPage,
289:             )));
290:         }
291:         return $pages;
292:     }
293: 
294:     /**
295:      * Navigation information of the prev button.
296:      *
297:      * @return Pinoco_Vars
298:      */
299:     public function get_prev()
300:     {
301:         if ($this->_currentPage > 1) {
302:             return Pinoco::newVars(array(
303:                 'enabled' => true,
304:                 'number'  => $this->_currentPage - 1,
305:                 'href'    => call_user_func($this->urlFormatCallable, $this, $this->_currentPage - 1),
306:             ));
307:         }
308:         else {
309:             return Pinoco::newVars(array(
310:                 'enabled' => false,
311:             ));
312:         }
313:     }
314: 
315:     /**
316:      * Navigation information of the next button.
317:      *
318:      * @return Pinoco_Vars
319:      */
320:     public function get_next()
321:     {
322:         if ($this->_currentPage < $this->totalPages) {
323:             return Pinoco::newVars(array(
324:                 'enabled' => true,
325:                 'number'  => $this->_currentPage + 1,
326:                 'href'    => call_user_func($this->urlFormatCallable, $this, $this->_currentPage + 1),
327:             ));
328:         }
329:         else {
330:             return Pinoco::newVars(array(
331:                 'enabled' => false,
332:             ));
333:         }
334:     }
335: }
336: 
Pinoco 0.8.0 Documentation API documentation generated by ApiGen 2.8.0