CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.10 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.10
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper
  • None

Classes

  • AclComponent
  • AuthComponent
  • CookieComponent
  • EmailComponent
  • FlashComponent
  • PaginatorComponent
  • RequestHandlerComponent
  • SecurityComponent
  • SessionComponent
  1: <?php
  2: /**
  3:  * Paginator Component
  4:  *
  5:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  6:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  7:  *
  8:  * Licensed under The MIT License
  9:  * For full copyright and license information, please see the LICENSE.txt
 10:  * Redistributions of files must retain the above copyright notice.
 11:  *
 12:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 13:  * @link          https://cakephp.org CakePHP(tm) Project
 14:  * @package       Cake.Controller.Component
 15:  * @since         CakePHP(tm) v 2.0
 16:  * @license       https://opensource.org/licenses/mit-license.php MIT License
 17:  */
 18: 
 19: App::uses('Component', 'Controller');
 20: App::uses('Hash', 'Utility');
 21: 
 22: /**
 23:  * This component is used to handle automatic model data pagination. The primary way to use this
 24:  * component is to call the paginate() method. There is a convenience wrapper on Controller as well.
 25:  *
 26:  * ### Configuring pagination
 27:  *
 28:  * You configure pagination using the PaginatorComponent::$settings. This allows you to configure
 29:  * the default pagination behavior in general or for a specific model. General settings are used when there
 30:  * are no specific model configuration, or the model you are paginating does not have specific settings.
 31:  *
 32:  * ```
 33:  *  $this->Paginator->settings = array(
 34:  *      'limit' => 20,
 35:  *      'maxLimit' => 100
 36:  *  );
 37:  * ```
 38:  *
 39:  * The above settings will be used to paginate any model. You can configure model specific settings by
 40:  * keying the settings with the model name.
 41:  *
 42:  * ```
 43:  *  $this->Paginator->settings = array(
 44:  *      'Post' => array(
 45:  *          'limit' => 20,
 46:  *          'maxLimit' => 100
 47:  *      ),
 48:  *      'Comment' => array( ... )
 49:  *  );
 50:  * ```
 51:  *
 52:  * This would allow you to have different pagination settings for `Comment` and `Post` models.
 53:  *
 54:  * #### Paginating with custom finders
 55:  *
 56:  * You can paginate with any find type defined on your model using the `findType` option.
 57:  *
 58:  * ```
 59:  * $this->Paginator->settings = array(
 60:  *      'Post' => array(
 61:  *          'findType' => 'popular'
 62:  *      )
 63:  * );
 64:  * ```
 65:  *
 66:  * Would paginate using the `find('popular')` method.
 67:  *
 68:  * @package       Cake.Controller.Component
 69:  * @link https://book.cakephp.org/2.0/en/core-libraries/components/pagination.html
 70:  */
 71: class PaginatorComponent extends Component {
 72: 
 73: /**
 74:  * Pagination settings. These settings control pagination at a general level.
 75:  * You can also define sub arrays for pagination settings for specific models.
 76:  *
 77:  * - `maxLimit` The maximum limit users can choose to view. Defaults to 100
 78:  * - `limit` The initial number of items per page. Defaults to 20.
 79:  * - `page` The starting page, defaults to 1.
 80:  * - `paramType` What type of parameters you want pagination to use?
 81:  *      - `named` Use named parameters / routed parameters.
 82:  *      - `querystring` Use query string parameters.
 83:  * - `queryScope` By using request parameter scopes you can paginate multiple queries in the same controller action.
 84:  *
 85:  * ```
 86:  * $paginator->paginate = array(
 87:  *  'Article' => array('queryScope' => 'articles'),
 88:  *  'Tag' => array('queryScope' => 'tags'),
 89:  * );
 90:  * ```
 91:  *
 92:  * Each of the above queries will use different query string parameter sets
 93:  * for pagination data. An example URL paginating both results would be:
 94:  *
 95:  * ```
 96:  * /dashboard/articles[page]:1/tags[page]:2
 97:  * ```
 98:  *
 99:  * @var array
100:  */
101:     public $settings = array(
102:         'page' => 1,
103:         'limit' => 20,
104:         'maxLimit' => 100,
105:         'paramType' => 'named',
106:         'queryScope' => null
107:     );
108: 
109: /**
110:  * A list of parameters users are allowed to set using request parameters. Modifying
111:  * this list will allow users to have more influence over pagination,
112:  * be careful with what you permit.
113:  *
114:  * @var array
115:  */
116:     public $whitelist = array(
117:         'limit', 'sort', 'page', 'direction'
118:     );
119: 
120: /**
121:  * Constructor
122:  *
123:  * @param ComponentCollection $collection A ComponentCollection this component can use to lazy load its components
124:  * @param array $settings Array of configuration settings.
125:  */
126:     public function __construct(ComponentCollection $collection, $settings = array()) {
127:         $settings = array_merge($this->settings, (array)$settings);
128:         $this->Controller = $collection->getController();
129:         parent::__construct($collection, $settings);
130:     }
131: 
132: /**
133:  * Handles automatic pagination of model records.
134:  *
135:  * @param Model|string $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
136:  * @param string|array $scope Additional find conditions to use while paginating
137:  * @param array $whitelist List of allowed fields for ordering. This allows you to prevent ordering
138:  *   on non-indexed, or undesirable columns. See PaginatorComponent::validateSort() for additional details
139:  *   on how the whitelisting and sort field validation works.
140:  * @return array Model query results
141:  * @throws MissingModelException
142:  * @throws NotFoundException
143:  */
144:     public function paginate($object = null, $scope = array(), $whitelist = array()) {
145:         if (is_array($object)) {
146:             $whitelist = $scope;
147:             $scope = $object;
148:             $object = null;
149:         }
150: 
151:         $object = $this->_getObject($object);
152: 
153:         if (!is_object($object)) {
154:             throw new MissingModelException($object);
155:         }
156: 
157:         $options = $this->mergeOptions($object->alias);
158:         $options = $this->validateSort($object, $options, $whitelist);
159:         $options = $this->checkLimit($options);
160: 
161:         $conditions = $fields = $order = $limit = $page = $recursive = null;
162: 
163:         if (!isset($options['conditions'])) {
164:             $options['conditions'] = array();
165:         }
166: 
167:         $type = 'all';
168: 
169:         if (isset($options[0])) {
170:             $type = $options[0];
171:             unset($options[0]);
172:         }
173: 
174:         extract($options);
175: 
176:         if (is_array($scope) && !empty($scope)) {
177:             $conditions = array_merge($conditions, $scope);
178:         } elseif (is_string($scope)) {
179:             $conditions = array($conditions, $scope);
180:         }
181:         if ($recursive === null) {
182:             $recursive = $object->recursive;
183:         }
184: 
185:         $extra = array_diff_key($options, compact(
186:             'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
187:         ));
188: 
189:         if (!empty($extra['findType'])) {
190:             $type = $extra['findType'];
191:             unset($extra['findType']);
192:         }
193: 
194:         if ($type !== 'all') {
195:             $extra['type'] = $type;
196:         }
197: 
198:         if ((int)$page < 1) {
199:             $page = 1;
200:         }
201:         $page = $options['page'] = (int)$page;
202: 
203:         if ($object->hasMethod('paginate')) {
204:             $results = $object->paginate(
205:                 $conditions, $fields, $order, $limit, $page, $recursive, $extra
206:             );
207:         } else {
208:             $parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
209:             if ($recursive != $object->recursive) {
210:                 $parameters['recursive'] = $recursive;
211:             }
212:             $results = $object->find($type, array_merge($parameters, $extra));
213:         }
214:         $defaults = $this->getDefaults($object->alias);
215:         unset($defaults[0]);
216: 
217:         if (!$results) {
218:             $count = 0;
219:         } elseif ($object->hasMethod('paginateCount')) {
220:             $count = $object->paginateCount($conditions, $recursive, $extra);
221:         } elseif ($page === 1 && count($results) < $limit) {
222:             $count = count($results);
223:         } else {
224:             $parameters = compact('conditions');
225:             if ($recursive != $object->recursive) {
226:                 $parameters['recursive'] = $recursive;
227:             }
228:             $count = $object->find('count', array_merge($parameters, $extra));
229:         }
230:         $pageCount = (int)ceil($count / $limit);
231:         $requestedPage = $page;
232:         $page = max(min($page, $pageCount), 1);
233: 
234:         $paging = array(
235:             'page' => $page,
236:             'current' => count($results),
237:             'count' => $count,
238:             'prevPage' => ($page > 1),
239:             'nextPage' => ($count > ($page * $limit)),
240:             'pageCount' => $pageCount,
241:             'order' => $order,
242:             'limit' => $limit,
243:             'options' => Hash::diff($options, $defaults),
244:             'paramType' => $options['paramType'],
245:             'queryScope' => $options['queryScope'],
246:         );
247: 
248:         if (!isset($this->Controller->request['paging'])) {
249:             $this->Controller->request['paging'] = array();
250:         }
251:         $this->Controller->request['paging'] = array_merge(
252:             (array)$this->Controller->request['paging'],
253:             array($object->alias => $paging)
254:         );
255: 
256:         if ($requestedPage > $page) {
257:             throw new NotFoundException();
258:         }
259: 
260:         if (!in_array('Paginator', $this->Controller->helpers) &&
261:             !array_key_exists('Paginator', $this->Controller->helpers)
262:         ) {
263:             $this->Controller->helpers[] = 'Paginator';
264:         }
265:         return $results;
266:     }
267: 
268: /**
269:  * Get the object pagination will occur on.
270:  *
271:  * @param string|Model $object The object you are looking for.
272:  * @return mixed The model object to paginate on.
273:  */
274:     protected function _getObject($object) {
275:         if (is_string($object)) {
276:             $assoc = null;
277:             if (strpos($object, '.') !== false) {
278:                 list($object, $assoc) = pluginSplit($object);
279:             }
280:             if ($assoc && isset($this->Controller->{$object}->{$assoc})) {
281:                 return $this->Controller->{$object}->{$assoc};
282:             }
283:             if ($assoc && isset($this->Controller->{$this->Controller->modelClass}->{$assoc})) {
284:                 return $this->Controller->{$this->Controller->modelClass}->{$assoc};
285:             }
286:             if (isset($this->Controller->{$object})) {
287:                 return $this->Controller->{$object};
288:             }
289:             if (isset($this->Controller->{$this->Controller->modelClass}->{$object})) {
290:                 return $this->Controller->{$this->Controller->modelClass}->{$object};
291:             }
292:         }
293:         if (empty($object) || $object === null) {
294:             if (isset($this->Controller->{$this->Controller->modelClass})) {
295:                 return $this->Controller->{$this->Controller->modelClass};
296:             }
297: 
298:             $className = null;
299:             $name = $this->Controller->uses[0];
300:             if (strpos($this->Controller->uses[0], '.') !== false) {
301:                 list($name, $className) = explode('.', $this->Controller->uses[0]);
302:             }
303:             if ($className) {
304:                 return $this->Controller->{$className};
305:             }
306: 
307:             return $this->Controller->{$name};
308:         }
309:         return $object;
310:     }
311: 
312: /**
313:  * Merges the various options that Pagination uses.
314:  * Pulls settings together from the following places:
315:  *
316:  * - General pagination settings
317:  * - Model specific settings.
318:  * - Request parameters
319:  *
320:  * The result of this method is the aggregate of all the option sets combined together. You can change
321:  * PaginatorComponent::$whitelist to modify which options/values can be set using request parameters.
322:  *
323:  * @param string $alias Model alias being paginated, if the general settings has a key with this value
324:  *   that key's settings will be used for pagination instead of the general ones.
325:  * @return array Array of merged options.
326:  */
327:     public function mergeOptions($alias) {
328:         $defaults = $this->getDefaults($alias);
329:         switch ($defaults['paramType']) {
330:             case 'named':
331:                 $request = $this->Controller->request->params['named'];
332:                 break;
333:             case 'querystring':
334:                 $request = $this->Controller->request->query;
335:                 break;
336:         }
337:         if ($defaults['queryScope']) {
338:             $request = Hash::get($request, $defaults['queryScope'], array());
339:         }
340:         $request = array_intersect_key($request, array_flip($this->whitelist));
341:         return array_merge($defaults, $request);
342:     }
343: 
344: /**
345:  * Get the default settings for a $model. If there are no settings for a specific model, the general settings
346:  * will be used.
347:  *
348:  * @param string $alias Model name to get default settings for.
349:  * @return array An array of pagination defaults for a model, or the general settings.
350:  */
351:     public function getDefaults($alias) {
352:         $defaults = $this->settings;
353:         if (isset($this->settings[$alias])) {
354:             $defaults = $this->settings[$alias];
355:         }
356:         $defaults += array(
357:             'page' => 1,
358:             'limit' => 20,
359:             'maxLimit' => 100,
360:             'paramType' => 'named',
361:             'queryScope' => null
362:         );
363:         return $defaults;
364:     }
365: 
366: /**
367:  * Validate that the desired sorting can be performed on the $object. Only fields or
368:  * virtualFields can be sorted on. The direction param will also be sanitized. Lastly
369:  * sort + direction keys will be converted into the model friendly order key.
370:  *
371:  * You can use the whitelist parameter to control which columns/fields are available for sorting.
372:  * This helps prevent users from ordering large result sets on un-indexed values.
373:  *
374:  * Any columns listed in the sort whitelist will be implicitly trusted. You can use this to sort
375:  * on synthetic columns, or columns added in custom find operations that may not exist in the schema.
376:  *
377:  * @param Model $object The model being paginated.
378:  * @param array $options The pagination options being used for this request.
379:  * @param array $whitelist The list of columns that can be used for sorting. If empty all keys are allowed.
380:  * @return array An array of options with sort + direction removed and replaced with order if possible.
381:  */
382:     public function validateSort(Model $object, array $options, array $whitelist = array()) {
383:         if (empty($options['order']) && is_array($object->order)) {
384:             $options['order'] = $object->order;
385:         }
386: 
387:         if (isset($options['sort'])) {
388:             $direction = null;
389:             if (isset($options['direction'])) {
390:                 $direction = strtolower($options['direction']);
391:             }
392:             if (!in_array($direction, array('asc', 'desc'))) {
393:                 $direction = 'asc';
394:             }
395:             $options['order'] = array($options['sort'] => $direction);
396:         }
397: 
398:         if (!empty($whitelist) && isset($options['order']) && is_array($options['order'])) {
399:             $field = key($options['order']);
400:             $inWhitelist = in_array($field, $whitelist, true);
401:             if (!$inWhitelist) {
402:                 $options['order'] = null;
403:             }
404:             return $options;
405:         }
406:         if (!empty($options['order']) && is_array($options['order'])) {
407:             $order = array();
408:             foreach ($options['order'] as $key => $value) {
409:                 if (is_int($key)) {
410:                     $field = explode(' ', $value);
411:                     $key = $field[0];
412:                     $value = count($field) === 2 ? trim($field[1]) : 'asc';
413:                 }
414:                 $field = $key;
415:                 $alias = $object->alias;
416:                 if (strpos($key, '.') !== false) {
417:                     list($alias, $field) = explode('.', $key);
418:                 }
419:                 $correctAlias = ($object->alias === $alias);
420: 
421:                 if ($correctAlias && $object->hasField($field)) {
422:                     $order[$object->alias . '.' . $field] = $value;
423:                 } elseif ($correctAlias && $object->hasField($key, true)) {
424:                     $order[$field] = $value;
425:                 } elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field, true)) {
426:                     $order[$alias . '.' . $field] = $value;
427:                 }
428:             }
429:             $options['order'] = $order;
430:         }
431: 
432:         return $options;
433:     }
434: 
435: /**
436:  * Check the limit parameter and ensure its within the maxLimit bounds.
437:  *
438:  * @param array $options An array of options with a limit key to be checked.
439:  * @return array An array of options for pagination
440:  */
441:     public function checkLimit(array $options) {
442:         $options['limit'] = (int)$options['limit'];
443:         if (empty($options['limit']) || $options['limit'] < 1) {
444:             $options['limit'] = 1;
445:         }
446:         $options['limit'] = min($options['limit'], $options['maxLimit']);
447:         return $options;
448:     }
449: 
450: }
451: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs