BatchRangeIterator.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\DB\Query;
  7. use Magento\Framework\DB\Adapter\AdapterInterface;
  8. use Magento\Framework\DB\Select;
  9. /**
  10. * Query batch range iterator
  11. *
  12. * It is uses to processing selects which will obtain values from $rangeField with relation one-to-many
  13. * This iterator make chunks with operator LIMIT...OFFSET,
  14. * starting with zero offset and finishing on OFFSET + LIMIT = TOTAL_COUNT
  15. *
  16. * @see \Magento\Framework\DB\Query\Generator
  17. * @see \Magento\Framework\DB\Query\BatchIteratorFactory
  18. * @see \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction
  19. * @see \Magento\Framework\DB\Adapter\Pdo\Mysql
  20. */
  21. class BatchRangeIterator implements BatchIteratorInterface
  22. {
  23. /**
  24. * @var Select
  25. */
  26. private $currentSelect;
  27. /**
  28. * @var string|array
  29. */
  30. private $rangeField;
  31. /**
  32. * @var string
  33. * @deprecated 102.0.0 unused class property
  34. */
  35. private $rangeFieldAlias;
  36. /**
  37. * @var int
  38. */
  39. private $batchSize;
  40. /**
  41. * @var AdapterInterface
  42. */
  43. private $connection;
  44. /**
  45. * @var int
  46. */
  47. private $currentOffset = 0;
  48. /**
  49. * @var int
  50. */
  51. private $totalItemCount;
  52. /**
  53. * @var int
  54. */
  55. private $iteration = 0;
  56. /**
  57. * @var Select
  58. */
  59. private $select;
  60. /**
  61. * @var string
  62. */
  63. private $correlationName;
  64. /**
  65. * @var bool
  66. */
  67. private $isValid = true;
  68. /**
  69. * Initialize dependencies.
  70. *
  71. * @param Select $select
  72. * @param int $batchSize
  73. * @param string $correlationName
  74. * @param string|array $rangeField
  75. * @param string $rangeFieldAlias @deprecated
  76. */
  77. public function __construct(
  78. Select $select,
  79. $batchSize,
  80. $correlationName,
  81. $rangeField,
  82. $rangeFieldAlias
  83. ) {
  84. $this->batchSize = $batchSize;
  85. $this->select = $select;
  86. $this->correlationName = $correlationName;
  87. $this->rangeField = $rangeField;
  88. $this->rangeFieldAlias = $rangeFieldAlias;
  89. $this->connection = $select->getConnection();
  90. }
  91. /**
  92. * Return the current element
  93. *
  94. * If we don't have sub-select we should create and remember it.
  95. *
  96. * @return Select
  97. */
  98. public function current()
  99. {
  100. if (null === $this->currentSelect) {
  101. $this->isValid = $this->currentOffset < $this->totalItemCount;
  102. $this->currentSelect = $this->initSelectObject();
  103. }
  104. return $this->currentSelect;
  105. }
  106. /**
  107. * Return the key of the current element
  108. *
  109. * Сan return the number of the current sub-select in the iteration.
  110. *
  111. * @return int
  112. */
  113. public function key()
  114. {
  115. return $this->iteration;
  116. }
  117. /**
  118. * Move forward to next sub-select
  119. *
  120. * Retrieve the next sub-select and move cursor to the next element.
  121. * Checks that the count of elements more than the sum of limit and offset.
  122. *
  123. * @return Select
  124. */
  125. public function next()
  126. {
  127. if (null === $this->currentSelect) {
  128. $this->current();
  129. }
  130. $this->isValid = $this->currentOffset < $this->totalItemCount;
  131. $select = $this->initSelectObject();
  132. if ($this->isValid) {
  133. $this->iteration++;
  134. $this->currentSelect = $select;
  135. } else {
  136. $this->currentSelect = null;
  137. }
  138. return $this->currentSelect;
  139. }
  140. /**
  141. * Rewind the BatchRangeIterator to the first element.
  142. *
  143. * Allows to start iteration from the beginning.
  144. *
  145. * @return void
  146. */
  147. public function rewind()
  148. {
  149. $this->currentSelect = null;
  150. $this->iteration = 0;
  151. $this->isValid = true;
  152. $this->totalItemCount = 0;
  153. }
  154. /**
  155. * Checks if current position is valid
  156. *
  157. * @return bool
  158. */
  159. public function valid()
  160. {
  161. return $this->isValid;
  162. }
  163. /**
  164. * Initialize select object
  165. *
  166. * Return sub-select which is limited by current batch value and return items from n page of SQL request.
  167. *
  168. * @return \Magento\Framework\DB\Select
  169. */
  170. private function initSelectObject()
  171. {
  172. $object = clone $this->select;
  173. if (!$this->totalItemCount) {
  174. $wrapperSelect = $this->connection->select();
  175. $wrapperSelect->from(
  176. $object,
  177. [
  178. new \Zend_Db_Expr('COUNT(*) as cnt')
  179. ]
  180. );
  181. $row = $this->connection->fetchRow($wrapperSelect);
  182. $this->totalItemCount = (int)$row['cnt'];
  183. }
  184. $rangeField = is_array($this->rangeField) ? $this->rangeField : [$this->rangeField];
  185. /**
  186. * Reset sort order section from origin select object
  187. */
  188. foreach ($rangeField as $field) {
  189. $object->order($this->correlationName . '.' . $field . ' ' . \Magento\Framework\DB\Select::SQL_ASC);
  190. }
  191. $object->limit($this->batchSize, $this->currentOffset);
  192. $this->currentOffset += $this->batchSize;
  193. return $object;
  194. }
  195. }