PagerSelectExtender.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <?php
  2. namespace Drupal\Core\Database\Query;
  3. use Drupal\Core\Database\Connection;
  4. /**
  5. * Query extender for pager queries.
  6. *
  7. * This is the "default" pager mechanism. It creates a paged query with a fixed
  8. * number of entries per page.
  9. *
  10. * When adding this extender along with other extenders, be sure to add
  11. * PagerSelectExtender last, so that its range and count are based on the full
  12. * query.
  13. */
  14. class PagerSelectExtender extends SelectExtender {
  15. /**
  16. * The highest element we've autogenerated so far.
  17. *
  18. * @var int
  19. */
  20. public static $maxElement = 0;
  21. /**
  22. * The number of elements per page to allow.
  23. *
  24. * @var int
  25. */
  26. protected $limit = 10;
  27. /**
  28. * The unique ID of this pager on this page.
  29. *
  30. * @var int
  31. */
  32. protected $element = NULL;
  33. /**
  34. * The count query that will be used for this pager.
  35. *
  36. * @var \Drupal\Core\Database\Query\SelectInterface
  37. */
  38. protected $customCountQuery = FALSE;
  39. public function __construct(SelectInterface $query, Connection $connection) {
  40. parent::__construct($query, $connection);
  41. // Add pager tag. Do this here to ensure that it is always added before
  42. // preExecute() is called.
  43. $this->addTag('pager');
  44. }
  45. /**
  46. * Override the execute method.
  47. *
  48. * Before we run the query, we need to add pager-based range() instructions
  49. * to it.
  50. */
  51. public function execute() {
  52. // By calling preExecute() here, we force it to preprocess the extender
  53. // object rather than just the base query object. That means
  54. // hook_query_alter() gets access to the extended object.
  55. if (!$this->preExecute($this)) {
  56. return NULL;
  57. }
  58. // A NULL limit is the "kill switch" for pager queries.
  59. if (empty($this->limit)) {
  60. return;
  61. }
  62. $this->ensureElement();
  63. $total_items = $this->getCountQuery()->execute()->fetchField();
  64. /** @var \Drupal\Core\Pager\PagerManagerInterface $pager_manager */
  65. $pager_manager = \Drupal::service('pager.manager');
  66. $pager = $pager_manager->createPager($total_items, $this->limit, $this->element);
  67. $this->range($pager->getCurrentPage() * $this->limit, $this->limit);
  68. // Now that we've added our pager-based range instructions, run the query normally.
  69. return $this->query->execute();
  70. }
  71. /**
  72. * Ensure that there is an element associated with this query.
  73. * If an element was not specified previously, then the value of the
  74. * $maxElement counter is taken, after which the counter is incremented.
  75. *
  76. * After running this method, access $this->element to get the element for this
  77. * query.
  78. */
  79. protected function ensureElement() {
  80. if (!isset($this->element)) {
  81. $this->element = self::$maxElement++;
  82. }
  83. }
  84. /**
  85. * Specify the count query object to use for this pager.
  86. *
  87. * You will rarely need to specify a count query directly. If not specified,
  88. * one is generated off of the pager query itself.
  89. *
  90. * @param \Drupal\Core\Database\Query\SelectInterface $query
  91. * The count query object. It must return a single row with a single column,
  92. * which is the total number of records.
  93. */
  94. public function setCountQuery(SelectInterface $query) {
  95. $this->customCountQuery = $query;
  96. }
  97. /**
  98. * Retrieve the count query for this pager.
  99. *
  100. * The count query may be specified manually or, by default, taken from the
  101. * query we are extending.
  102. *
  103. * @return \Drupal\Core\Database\Query\SelectInterface
  104. * A count query object.
  105. */
  106. public function getCountQuery() {
  107. if ($this->customCountQuery) {
  108. return $this->customCountQuery;
  109. }
  110. else {
  111. return $this->query->countQuery();
  112. }
  113. }
  114. /**
  115. * Specify the maximum number of elements per page for this query.
  116. *
  117. * The default if not specified is 10 items per page.
  118. *
  119. * @param int|false $limit
  120. * An integer specifying the number of elements per page. If passed a false
  121. * value (FALSE, 0, NULL), the pager is disabled.
  122. */
  123. public function limit($limit = 10) {
  124. $this->limit = $limit;
  125. return $this;
  126. }
  127. /**
  128. * Specify the element ID for this pager query.
  129. *
  130. * The element is used to differentiate different pager queries on the same
  131. * page so that they may be operated independently. If you do not specify an
  132. * element, every pager query on the page will get a unique element. If for
  133. * whatever reason you want to explicitly define an element for a given query,
  134. * you may do so here.
  135. *
  136. * Setting the element here also increments the static $maxElement counter,
  137. * which is used for determining the $element when there's none specified.
  138. *
  139. * Note that no collision detection is done when setting an element ID
  140. * explicitly, so it is possible for two pagers to end up using the same ID
  141. * if both are set explicitly.
  142. *
  143. * @param $element
  144. * Element ID that is used to differentiate different pager queries.
  145. */
  146. public function element($element) {
  147. $this->element = $element;
  148. if ($element >= self::$maxElement) {
  149. self::$maxElement = $element + 1;
  150. }
  151. return $this;
  152. }
  153. }