Commit 64cf795a authored by plach's avatar plach

Issue #2044435 by mondrake, kim.pepper, Mile23, andypost, martin107, daffie,...

Issue #2044435 by mondrake, kim.pepper, Mile23, andypost, martin107, daffie, jibran, beejeebus, alexpott, larowlan, dawehner, catch, pwolanin: Convert pager.inc to a service

(cherry picked from commit 38741fcb)
parent b231e18d
......@@ -106,7 +106,7 @@ services:
- { name: cache.context }
cache_context.url.query_args.pagers:
class: Drupal\Core\Cache\Context\PagersCacheContext
arguments: ['@request_stack']
arguments: ['@pager.parameters']
tags:
- { name: cache.context }
......@@ -1742,3 +1742,9 @@ services:
arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%']
tags:
- { name: backend_overridable }
pager.manager:
class: Drupal\Core\Pager\PagerManager
arguments: ['@pager.parameters']
pager.parameters:
class: Drupal\Core\Pager\PagerParameters
arguments: ['@request_stack']
......@@ -5,6 +5,8 @@
* These are the global variables that Drupal uses.
*/
use Drupal\Component\Utility\DeprecatedArray;
/**
* The insecure base URL of the Drupal installation.
*
......@@ -81,33 +83,53 @@
*
* The array index is the pager element index (0 by default).
*
* @see pager_default_initialize()
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
* directly set or get values from this array. Use the pager.manager service
* instead.
*
* @see https://www.drupal.org/node/2779457
* @see \Drupal\Core\Pager\PagerManagerInterface
*/
global $pager_limits;
$GLOBALS['pager_limits'] = new DeprecatedArray([], 'Global variable $pager_limits is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
/**
* Array of current page numbers for each pager.
*
* The array index is the pager element index (0 by default).
*
* @see pager_default_initialize()
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
* directly set or get values from this array. Use the pager.manager service
* instead.
*
* @see https://www.drupal.org/node/2779457
* @see \Drupal\Core\Pager\PagerManagerInterface
*/
global $pager_page_array;
$GLOBALS['pager_page_array'] = new DeprecatedArray([], 'Global variable $pager_page_array is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
/**
* Array of the total number of pages for each pager.
*
* The array index is the pager element index (0 by default).
*
* @see pager_default_initialize()
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
* directly set or get values from this array. Use the pager.manager service
* instead.
*
* @see https://www.drupal.org/node/2779457
* @see \Drupal\Core\Pager\PagerManagerInterface
*/
global $pager_total;
$GLOBALS['pager_total'] = new DeprecatedArray([], 'Global variable $pager_total is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
/**
* Array of the total number of items for each pager.
*
* The array index is the pager element index (0 by default).
*
* @see pager_default_initialize()
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
* directly set or get values from this array. Use the pager.manager service
* instead.
*
* @see https://www.drupal.org/node/2779457
* @see \Drupal\Core\Pager\PagerManagerInterface
*/
global $pager_total_items;
$GLOBALS['pager_total_items'] = new DeprecatedArray([], 'Global variable $pager_total_items is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
This diff is collapsed.
<?php
namespace Drupal\Component\Utility;
/**
* An array that triggers a deprecation warning when accessed.
*/
class DeprecatedArray implements \ArrayAccess {
/**
* The array values.
*
* @var array
*/
protected $values = [];
/**
* The deprecation message.
*
* @var string
*/
protected $message;
/**
* DeprecatedArray constructor.
*
* @param array $values
* The array values.
* @param $message
* The deprecation message.
*/
public function __construct(array $values, $message) {
$this->values = $values;
$this->message = $message;
}
/**
* {@inheritdoc}
*/
public function offsetExists($offset) {
@trigger_error($this->message, E_USER_DEPRECATED);
return isset($this->values[$offset]);
}
/**
* {@inheritdoc}
*/
public function offsetGet($offset) {
@trigger_error($this->message, E_USER_DEPRECATED);
return $this->values[$offset] ?? NULL;
}
/**
* {@inheritdoc}
*/
public function offsetSet($offset, $value) {
@trigger_error($this->message, E_USER_DEPRECATED);
return $this->values[$offset] = $value;
}
/**
* {@inheritdoc}
*/
public function offsetUnset($offset) {
@trigger_error($this->message, E_USER_DEPRECATED);
unset($this->values[$offset]);
}
}
......@@ -3,6 +3,8 @@
namespace Drupal\Core\Cache\Context;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
use Drupal\Core\Pager\PagerParametersInterface;
/**
* Defines a cache context for "per page in a pager" caching.
......@@ -11,7 +13,35 @@
* Calculated cache context ID: 'url.query_args.pagers:%pager_id', e.g.
* 'url.query_args.pagers:1' (to vary by the pager with ID 1).
*/
class PagersCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
class PagersCacheContext implements CalculatedCacheContextInterface {
use DeprecatedServicePropertyTrait;
/**
* {@inheritdoc}
*/
protected $deprecatedProperties = ['requestStack' => 'request_stack'];
/**
* The pager parameters.
*
* @var \Drupal\Core\Pager\PagerParametersInterface
*/
protected $pagerParams;
/**
* Constructs a new PagersCacheContext object.
*
* @param \Drupal\Core\Pager\PagerParametersInterface $pager_params
* The pager parameters.
*/
public function __construct($pager_params) {
if (!($pager_params instanceof PagerParametersInterface)) {
@trigger_error('Calling ' . __METHOD__ . ' with a $pager_params argument that does not implement \Drupal\Core\Pager\PagerParametersInterface is deprecated in drupal:8.8.0 and is required in drupal:9.0.0. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
$pager_params = \Drupal::service('pager.parameters');
}
$this->pagerParams = $pager_params;
}
/**
* {@inheritdoc}
......@@ -23,16 +53,16 @@ public static function getLabel() {
/**
* {@inheritdoc}
*
* @see pager_find_page()
* @see \Drupal\Core\Pager\PagerParametersInterface::findPage()
*/
public function getContext($pager_id = NULL) {
// The value of the 'page' query argument contains the information that
// controls *all* pagers.
if ($pager_id === NULL) {
return $this->requestStack->getCurrentRequest()->query->get('page', '');
return $this->pagerParams->getPagerParameter();
}
return $pager_id . '.' . pager_find_page($pager_id);
return $pager_id . '.' . $this->pagerParams->findPage($pager_id);
}
/**
......
......@@ -73,8 +73,10 @@ public function execute() {
$this->ensureElement();
$total_items = $this->getCountQuery()->execute()->fetchField();
$current_page = pager_default_initialize($total_items, $this->limit, $this->element);
$this->range($current_page * $this->limit, $this->limit);
/** @var \Drupal\Core\Pager\PagerManagerInterface $pager_manager */
$pager_manager = \Drupal::service('pager.manager');
$pager = $pager_manager->createPager($total_items, $this->limit, $this->element);
$this->range($pager->getCurrentPage() * $this->limit, $this->limit);
// Now that we've added our pager-based range instructions, run the query normally.
return $this->query->execute();
......
......@@ -310,11 +310,11 @@ public function pager($limit = 10, $element = NULL) {
*/
protected function initializePager() {
if ($this->pager && !empty($this->pager['limit']) && !$this->count) {
$page = pager_find_page($this->pager['element']);
$page = \Drupal::service('pager.parameters')->findPage($this->pager['element']);
$count_query = clone $this;
$this->pager['total'] = $count_query->count()->execute();
$this->pager['start'] = $page * $this->pager['limit'];
pager_default_initialize($this->pager['total'], $this->pager['limit'], $this->pager['element']);
\Drupal::service('pager.manager')->createPager($this->pager['total'], $this->pager['limit'], $this->pager['element']);
$this->range($this->pager['start'], $this->pager['limit']);
}
}
......
<?php
namespace Drupal\Core\Pager;
/**
* A value object that represents a pager.
*/
class Pager {
/**
* The total number of items .
*
* @var int
*/
protected $totalItems;
/**
* The total number of pages.
*
* @var int
*/
protected $totalPages;
/**
* The current page of the pager.
*
* @var int
*/
protected $currentPage;
/**
* The maximum number of items per page.
*
* @var int
*/
protected $limit;
/**
* Pager constructor.
*
* @param int $totalItems
* The total number of items.
* @param int $limit
* The maximum number of items per page.
* @param int $currentPage
* The current page.
*/
public function __construct($totalItems, $limit, $currentPage = 0) {
$this->totalItems = $totalItems;
$this->limit = $limit;
$this->setTotalPages($totalItems, $limit);
$this->setCurrentPage($currentPage);
}
/**
* Sets the current page to a valid value within range.
*
* If a page that does not correspond to the actual range of the result set
* was provided, this function will set the closest page actually within
* the result set.
*
* @param int $currentPage
* (optional) The current page.
*/
protected function setCurrentPage($currentPage = 0) {
$this->currentPage = max(0, min($currentPage, $this->getTotalPages() - 1));
}
/**
* Sets the total number of pages.
*
* @param int $totalItems
* The total number of items.
* @param int $limit
* The maximum number of items per page.
*/
protected function setTotalPages($totalItems, $limit) {
$this->totalPages = (int) ceil($totalItems / $limit);
}
/**
* Gets the total number of items.
*
* @return int
* The total number of items.
*/
public function getTotalItems() {
return $this->totalItems;
}
/**
* Gets the total number of pages.
*
* @return int
* The total number of pages.
*/
public function getTotalPages() {
return $this->totalPages;
}
/**
* Gets the current page.
*
* @return int
* The current page.
*/
public function getCurrentPage() {
return $this->currentPage;
}
/**
* Gets the maximum number of items per page.
*
* @return int
* The the maximum number of items per page.
*/
public function getLimit() {
return $this->limit;
}
}
<?php
namespace Drupal\Core\Pager;
use Drupal\Component\Utility\DeprecatedArray;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* Provides a manager for pagers.
*
* Pagers are cached, and can be retrieved when rendering.
*/
class PagerManager implements PagerManagerInterface {
use DependencySerializationTrait;
/**
* The pager parameters.
*
* @var \Drupal\Core\Pager\PagerParametersInterface
*/
protected $pagerParams;
/**
* An associative array of pagers.
*
* Implemented as an array consisting of:
* - key: the element id integer.
* - value: a \Drupal\Core\Pager\Pager.
*
* @var array
*/
protected $pagers;
/**
* Construct a PagerManager object.
*
* @param \Drupal\Core\Pager\PagerParametersInterface $pager_params
* The pager parameters.
*/
public function __construct(PagerParametersInterface $pager_params) {
$this->pagerParams = $pager_params;
}
/**
* {@inheritdoc}
*/
public function createPager($total, $limit, $element = 0) {
$currentPage = $this->pagerParams->findPage($element);
$pager = new Pager($total, $limit, $currentPage);
$this->setPager($pager, $element);
return $pager;
}
/**
* {@inheritdoc}
*/
public function getPager($element = 0) {
return isset($this->pagers[$element]) ? $this->pagers[$element] : NULL;
}
/**
* {@inheritdoc}
*/
public function getUpdatedParameters(array $query, $element, $index) {
// Build the 'page' query parameter. This is built based on the current
// page of each pager element (or NULL if the pager is not set), with the
// exception of the requested page index for the current element.
$element_pages = [];
$max = $this->getMaxPagerElementId();
for ($i = 0; $i <= $max; $i++) {
$currentPage = ($pager = $this->getPager($i)) ? $pager->getCurrentPage() : NULL;
$element_pages[] = ($i == $element) ? $index : $currentPage;
}
$query['page'] = implode(',', $element_pages);
// Merge the query parameters passed to this function with the parameters
// from the current request. In case of collision, the parameters passed
// into this function take precedence.
if ($current_query = $this->pagerParams->getQueryParameters()) {
$query = array_merge($current_query, $query);
}
return $query;
}
/**
* Gets the extent of the pager page element IDs.
*
* @return int
* The maximum element ID available, -1 if there are no elements.
*/
protected function getMaxPagerElementId() {
return empty($this->pagers) ? -1 : max(array_keys($this->pagers));
}
/**
* Saves a pager to the static cache.
*
* @param \Drupal\Core\Pager\Pager $pager
* The pager.
* @param int $element
* The pager index.
*/
protected function setPager(Pager $pager, $element = 0) {
$this->pagers[$element] = $pager;
$this->updateGlobals();
}
/**
* Updates global variables with a pager data for backwards compatibility.
*/
protected function updateGlobals() {
$pager_total_items = [];
$pager_total = [];
$pager_page_array = [];
$pager_limits = [];
/** @var $pager \Drupal\Core\Pager\Pager */
foreach ($this->pagers as $pager_id => $pager) {
$pager_total_items[$pager_id] = $pager->getTotalItems();
$pager_total[$pager_id] = $pager->getTotalPages();
$pager_page_array[$pager_id] = $pager->getCurrentPage();
$pager_limits[$pager_id] = $pager->getLimit();
}
$GLOBALS['pager_total_items'] = new DeprecatedArray($pager_total_items, 'Global variable $pager_total_items is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
$GLOBALS['pager_total'] = new DeprecatedArray($pager_total, 'Global variable $pager_total is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
$GLOBALS['pager_page_array'] = new DeprecatedArray($pager_page_array, 'Global variable $pager_page_array is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
$GLOBALS['pager_limits'] = new DeprecatedArray($pager_limits, 'Global variable $pager_limits is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
}
}
<?php
namespace Drupal\Core\Pager;
/**
* This is a service for pager information.
*
* The pager.manager service manages the pager information which will eventually
* be rendered into pager elements in the response. To gather information
* related to pager information in the request, use the pager.parameters
* service.
*
* Since there can be multiple pagers per requested page, each one is
* represented by an 'element' ID. This is an integer. It represents the index
* of the pager element within the 'page' query. The value of the element is an
* integer telling us the current page number for that pager.
*
* This class generally replaces the functions in core/includes/pager.inc. Those
* functions use globals to store data which they all use. Since we require
* backwards compatibility with this behavior, this class presents a public API
* for using pager information, which is implemented using the same globals as a
* 'backend.'
*
* @see \Drupal\Core\Pager\PagerParametersInterface
*/
interface PagerManagerInterface {
/**
* Initializes a pager.
*
* This function sets up the necessary variables so that the render system
* will correctly process #type 'pager' render arrays to output pagers that
* correspond to the items being displayed.
*
* If the items being displayed result from a database query performed using
* Drupal's database API, and if you have control over the construction of the
* database query, you do not need to call this function directly; instead,
* you can extend the query object with the 'PagerSelectExtender' extender
* before executing it. For example:
* @code
* $query = db_select('some_table')
* ->extend('Drupal\Core\Database\Query\PagerSelectExtender');
* @endcode
*
* However, if you are using a different method for generating the items to be
* paged through, then you should call this service in preparation.
*
* The following example shows how this service can be used in a controller
* that invokes an external datastore with an SQL-like syntax:
* @code
* // First find the total number of items and initialize the pager.
* $where = "status = 1";
* $total = mymodule_select("SELECT COUNT(*) FROM data " . $where)->result();
* $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
* $pager = \Drupal::service('pager.manager')->createPager($total, $num_per_page);
* $page = $pager->getCurrentPage();
*
* // Next, retrieve the items for the current page and put them into a
* // render array.
* $offset = $num_per_page * $page;
* $result = mymodule_select("SELECT * FROM data " . $where . " LIMIT %d, %d", $offset, $num_per_page)->fetchAll();
* $render = [];
* $render[] = [
* '#theme' => 'mymodule_results',
* '#result' => $result,
* ];
*
* // Finally, add the pager to the render array, and return.
* $render[] = ['#type' => 'pager'];
* return $render;
* @endcode
*
* A second example involves a controller that invokes an external search
* service where the total number of matching results is provided as part of
* the returned set (so that we do not need a separate query in order to
* obtain this information). Here, we call PagerManagerInterface->findPage()
* to calculate the desired offset before the search is invoked:
* @code
*
* // Perform the query, using the requested offset from
* // PagerManagerInterface::findPage(). This comes from a URL parameter, so
* // here we are assuming that the URL parameter corresponds to an actual
* // page of results that will exist within the set.
* $pager_parameters = \Drupal::service('pager.parameters');
* $page = $pager_parameters->findPage();
* $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
* $offset = $num_per_page * $page;
* $result = mymodule_remote_search($keywords, $offset, $num_per_page);
*
* // Now that we have the total number of results, initialize the pager.
* $pager_manager = \Drupal::service('pager.manager');
* $pager_manager->createPager($result->total, $num_per_page);
*
* // Create a render array with the search results.
* $render = [];
* $render[] = [
* '#theme' => 'search_results',
* '#results' => $result->data,
* '#type' => 'remote',
* ];
*
* // Finally, add the pager to the render array, and return.
* $render[] = ['#type' => 'pager'];
* return $render;
* @endcode
*
* @param int $total
* The total number of items to be paged.
* @param int $limit
* The number of items the calling code will display per page.
* @param int $element
* (optional) An integer to distinguish between multiple pagers on one page.
*
* @return \Drupal\Core\Pager\Pager
* The pager.
*/
public function createPager($total, $limit, $element = 0);
/**
* Gets a pager from the static cache.
*
* @param int $element
* The pager element index.
*
* @return \Drupal\Core\Pager\Pager|null
* The pager, or null if not found.
*/
public function getPager($element = 0);
/**
* Gets the URL query parameter array of a pager link.
*
* Adds to or adjusts the 'page' URL query parameter so that if you follow the
* link, you'll get page $index for pager $element on the page.
*
* The 'page' URL query parameter is a comma-delimited string, where each
* value is the target content page for the corresponding pager $element. For
* instance, if we have 5 pagers on a single page, and we want to have a link
* to a page that should display the 6th content page for the 3rd pager, and
* the 1st content page for all the other pagers, then the URL query will look
* like this: ?page=0,0,5,0,0 (page numbering starts at zero).
*
* @param array $query
* An associative array of URL query parameters to add to.
* @param int $element
* An integer to distinguish between multiple pagers on one page.
* @param int $index
* The index of the target page, for the given element, in the pager array.
*
* @return array
* The altered $query parameter array.
*/
public function getUpdatedParameters(array $query, $element, $index);
}
<?php
namespace Drupal\Core\Pager;
use Drupal\Component\Utility\UrlHelper;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Provides pager information contained within the current request.
*
* @see \Drupal\Core\Pager\PagerManagerInterface
*/
class PagerParameters implements PagerParametersInterface {
/**
* The HTTP request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* Construct a PagerManager object.
*
* @param \Symfony\Component\HttpFoundation\RequestStack $stack
* The current HTTP request stack.
*/
public function __construct(RequestStack $stack) {
$this->requestStack = $stack;
}
/**
* {@inheritdoc}
*/
public function getQueryParameters() {
$request = $this->requestStack->getCurrentRequest();
if ($request) {
return UrlHelper::filterQueryParameters(
$request->query->all(), ['page']
);
}
return [];
}
/**
* {@inheritdoc}
*/
public function findPage($pager_id = 0) {
$pages = $this->getPagerQuery();
return (int) ($pages[$pager_id] ?? 0);
}
/**
* {@inheritdoc}
*/
public function getPagerQuery() {
$query = $this->getPagerParameter();
return !empty($query) ? explode(',', $query) : [];
}