artabro/wire/core/PaginatedArray.php
2024-08-27 11:35:37 +02:00

332 lines
9.6 KiB
PHP

<?php namespace ProcessWire;
/**
* ProcessWire Paginated WireArray
*
* Like WireArray, but with the additional methods and properties needed for WirePaginatable interface.
*
* #pw-summary PaginatedArray is a type of WireArray that supports pagination of items within it.
* #pw-body =
* Here you will see methods specific to the pagination aspects of this class only. For full details on
* available methods outside of pagination, please see the `WireArray` class. The most common type of
* PaginatedArray is a `PageArray`.
* #pw-body
* #pw-summary-manipulation In most cases you will not need these manipulation methods as core API calls already take care of this.
*
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
* https://processwire.com
*
* @method string renderPager(array $options = array()) Renders pagination, when MarkupPageArray module installed
*
*/
class PaginatedArray extends WireArray implements WirePaginatable {
/**
* Total number of items, including those here and others that aren't, but may be here in pagination.
*
* @var int
*
*/
protected $numTotal = 0;
/**
* If this WireArray is a partial representation of a larger set, this will contain the max number of items allowed to be
* present/loaded in the WireArray at once.
*
* May vary from count() when on the last page of a result set.
* As a result, paging routines should refer to their own itemsPerPage rather than count().
* Applicable for paginated result sets. This number is not enforced for adding items to this WireArray.
*
* @var int
*
*/
protected $numLimit = 0;
/**
* If this WireArray is a partial representation of a larger set, this will contain the starting result number if previous results preceded it.
*
* @var int
*
*/
protected $numStart = 0;
/**
* Set the total number of items, if more than are in the WireArray.
*
* #pw-group-manipulation
*
* @param int $total
* @return $this
*
*/
public function setTotal($total) {
$this->numTotal = (int) $total;
return $this;
}
/**
* Get the total number of items in the WireArray, including all paginations.
*
* If no limit is used, this returns total number of items currently in the WireArray,
* which would be the same as the `WireArray::count()` value. But when a limit is
* used, this number will typically be larger than the count, as it includes all
* items across all paginations, whether currently present or not.
*
* #pw-group-retrieval
*
* @return int Total number of items across all paginations.
*
*/
public function getTotal() {
$total = $this->numTotal;
if(!$total) $total = $this->count();
return $total;
}
/**
* Set the limit that was used in pagination.
*
* #pw-group-manipulation
*
* @param int $numLimit
* @return $this
*
*/
public function setLimit($numLimit) {
$this->numLimit = (int) $numLimit;
return $this;
}
/**
* Get the limit that was used in pagination.
*
* If no limit was set, then it returns the number of items currently in this WireArray.
*
* #pw-group-retrieval
*
* @return int
*
*/
public function getLimit() {
return $this->numLimit;
}
/**
* Set the starting offset number to use for pagination.
*
* This is typically the current page number (minus 1) multiplied by limit setting.
*
* #pw-group-manipulation
*
* @param int $numStart
* @return $this
*
*/
public function setStart($numStart) {
$this->numStart = (int) $numStart;
return $this;
}
/**
* Get the starting offset number that was used for pagination.
*
* #pw-group-retrieval
*
* @return int
*
*/
public function getStart() {
return $this->numStart;
}
/**
* Does this WireArray have more than one pagination?
*
* #pw-group-other
*
* @return bool
* @since 3.0.120
*
*/
public function hasPagination() {
return $this->getTotal() > 0 && $this->count() < $this->getTotal();
}
/**
* Is there a next pagination containing more items in this PaginatedArray after the current one?
*
* #pw-group-other
*
* @return bool
* @since 3.0.120
*
*/
public function hasNextPagination() {
return $this->getStart() + $this->count() < $this->getTotal();
}
/**
* Is there a previous pagination before the current one?
*
* #pw-group-other
*
* @return bool
* @since 3.0.120
*
*/
public function hasPrevPagination() {
return $this->getStart() > 0;
}
/**
* Get a property of the PageArray
*
* These map to functions from the array and are here for convenience.
* Properties include count, total, start, limit, last, first, keys, values,
* These can also be accessed by direct reference, i.e. `$items->limit`.
*
* Please see the `WireArray::getProperty()` method for full details on
* non-pagination related properties that can be retrieved through this.
*
* #pw-group-other
*
* @param string $property Name of property you want to retrieve, can be any of the following:
* - `count` (int): Count of items currently present.
* - `total` (int): Total quantity of items across all paginations.
* - `start` (int): Current start index for pagination.
* - `limit` (int): Current limit used for pagination.
* - `last` (mixed): Last item in the WireArray.
* - `first` (mixed): First item in the WireArray.
* @return mixed Value of requested property.
*
*/
public function getProperty($property) {
static $properties = array(
// property => method to map to
'total' => 'getTotal',
'start' => 'getStart',
'limit' => 'getLimit',
);
if(!in_array($property, $properties)) return parent::getProperty($property);
$func = $properties[$property];
return $this->$func();
}
/**
* Get a "1 to 10 of 50" type of string useful for pagination headlines.
*
* This returns a string of `1 to 10 of 30` (items) or `1 of 10` (pages) for example.
*
* Since 3.0.108 you can optionally replace either of the arguments with an $options o
* array instead, and you can specify any of these options:
*
* - `label` (string): Label to use for items, same as $label argument (default='').
* - `zeroLabel` (string): Alternate label to use if there are zero items, since 3.0.127 (default='').
* - `usePageNum` (bool): Specify true to show page numbers rather than item numbers (default=false).
* - `count` (int): Use this count rather than count in this PaginatedArray (default=-1, disabled).
* - `start` (int): Use this start rather than start in this PaginatedArray (default=-1, disabled).
* - `limit` (int): Use this limit rather than limit in this PaginatedArray (default=-1, disabled).
* - `total` (int): Use this total rather than total in this PaginatedArray (default=-1, disabled).
*
* ~~~~~
* // Get string like "Items 1 to 25 of 500"
* echo $items->getPaginationString('Items');
*
* // Get string like "Page 1 of 10"
* echo $items->getPaginationString('Page', true);
*
* // Get string where you specify all options
* echo $items->getPaginationString(array(
* 'label' => 'Items',
* 'zeroLabel' => 'No items found', // 3.0.127+ only
* 'usePageNum' => false,
* 'count' => 10,
* 'start' => 0,
* 'limit' => 10,
* 'total' => 100
* ));
* ~~~~~
*
* #pw-group-other
*
* @param string|array $label Label to identify item type, i.e. "Items" or "Page", etc. (default=empty).
* @param bool|array $usePageNum Specify true to show page numbers rather than item numbers (default=false).
* @return string Formatted string
*
*/
public function getPaginationString($label = '', $usePageNum = false) {
$options = array(
'label' => is_string($label) ? $label : '',
'zeroLabel' => '',
'usePageNum' => is_bool($usePageNum) ? $usePageNum : false,
'count' => -1,
'start' => -1,
'limit' => -1,
'total' => -1
);
if(is_array($label)) $options = array_merge($options, $label);
if(is_array($usePageNum)) $options = array_merge($options, $usePageNum);
$label = $options['label'];
$usePageNum = $options['usePageNum'];
$count = $options['count'] > -1 ? $options['count'] : $this->count();
$start = $options['start'] > -1 ? $options['start'] : $this->getStart();
$limit = $options['limit'] > -1 ? $options['limit'] : $this->getLimit();
$total = $options['total'] > -1 ? $options['total'] : $this->getTotal();
if($count > $total) $total = $count;
if(empty($total) && !empty($options['zeroLabel'])) {
$str = $options['zeroLabel'];
} else if($usePageNum) {
$pageNum = $start ? ($start / $limit) + 1 : 1;
$totalPages = ceil($total / $limit);
if(!$totalPages) $pageNum = 0;
$str = sprintf($this->_('%1$s %2$d of %3$d'), $label, $pageNum, $totalPages); // Page quantity, i.e. Page 1 of 3
} else {
if($limit && $count > $limit) $count = $limit;
$end = $start + $count;
if($end > $total) $total = $end;
$start++; // make 1 based rather than 0 based...
if($end == 0) $start = 0; // ...unless there are no items
$str = sprintf($this->_('%1$s %2$d to %3$d of %4$d'), $label, $start, $end, $total); // Pagination item quantity, i.e. Items 1 to 10 of 50
}
return trim($str);
}
/**
* debugInfo PHP 5.6+ magic method
*
* @return array
*
*/
public function __debugInfo() {
$limit = $this->getLimit();
$count = $this->count();
$total = $this->getTotal();
if($limit || $total > $count) {
$info = array(
'count' => $count,
'total' => $total,
'start' => $this->getStart(),
'limit' => $limit,
'pager' => $this->getPaginationString(),
);
$info = array_merge($info, parent::__debugInfo());
} else {
$info = parent::__debugInfo();
}
return $info;
}
}