PagerSelectExtender.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. $current_page = pager_default_initialize($total_items, $this->limit, $this->element);
  65. $this->range($current_page * $this->limit, $this->limit);
  66. // Now that we've added our pager-based range instructions, run the query normally.
  67. return $this->query->execute();
  68. }
  69. /**
  70. * Ensure that there is an element associated with this query.
  71. * If an element was not specified previously, then the value of the
  72. * $maxElement counter is taken, after which the counter is incremented.
  73. *
  74. * After running this method, access $this->element to get the element for this
  75. * query.
  76. */
  77. protected function ensureElement() {
  78. if (!isset($this->element)) {
  79. $this->element = self::$maxElement++;
  80. }
  81. }
  82. /**
  83. * Specify the count query object to use for this pager.
  84. *
  85. * You will rarely need to specify a count query directly. If not specified,
  86. * one is generated off of the pager query itself.
  87. *
  88. * @param \Drupal\Core\Database\Query\SelectInterface $query
  89. * The count query object. It must return a single row with a single column,
  90. * which is the total number of records.
  91. */
  92. public function setCountQuery(SelectInterface $query) {
  93. $this->customCountQuery = $query;
  94. }
  95. /**
  96. * Retrieve the count query for this pager.
  97. *
  98. * The count query may be specified manually or, by default, taken from the
  99. * query we are extending.
  100. *
  101. * @return \Drupal\Core\Database\Query\SelectInterface
  102. * A count query object.
  103. */
  104. public function getCountQuery() {
  105. if ($this->customCountQuery) {
  106. return $this->customCountQuery;
  107. }
  108. else {
  109. return $this->query->countQuery();
  110. }
  111. }
  112. /**
  113. * Specify the maximum number of elements per page for this query.
  114. *
  115. * The default if not specified is 10 items per page.
  116. *
  117. * @param int|false $limit
  118. * An integer specifying the number of elements per page. If passed a false
  119. * value (FALSE, 0, NULL), the pager is disabled.
  120. */
  121. public function limit($limit = 10) {
  122. $this->limit = $limit;
  123. return $this;
  124. }
  125. /**
  126. * Specify the element ID for this pager query.
  127. *
  128. * The element is used to differentiate different pager queries on the same
  129. * page so that they may be operated independently. If you do not specify an
  130. * element, every pager query on the page will get a unique element. If for
  131. * whatever reason you want to explicitly define an element for a given query,
  132. * you may do so here.
  133. *
  134. * Setting the element here also increments the static $maxElement counter,
  135. * which is used for determining the $element when there's none specified.
  136. *
  137. * Note that no collision detection is done when setting an element ID
  138. * explicitly, so it is possible for two pagers to end up using the same ID
  139. * if both are set explicitly.
  140. *
  141. * @param $element
  142. * Element ID that is used to differentiate different pager queries.
  143. */
  144. public function element($element) {
  145. $this->element = $element;
  146. if ($element >= self::$maxElement) {
  147. self::$maxElement = $element + 1;
  148. }
  149. return $this;
  150. }
  151. }