180 lines
4.8KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\db;
  8. use yii\base\Object;
  9. /**
  10. * BatchQueryResult represents a batch query from which you can retrieve data in batches.
  11. *
  12. * You usually do not instantiate BatchQueryResult directly. Instead, you obtain it by
  13. * calling [[Query::batch()]] or [[Query::each()]]. Because BatchQueryResult implements the [[\Iterator]] interface,
  14. * you can iterate it to obtain a batch of data in each iteration. For example,
  15. *
  16. * ```php
  17. * $query = (new Query)->from('user');
  18. * foreach ($query->batch() as $i => $users) {
  19. * // $users represents the rows in the $i-th batch
  20. * }
  21. * foreach ($query->each() as $user) {
  22. * }
  23. * ```
  24. *
  25. * @author Qiang Xue <qiang.xue@gmail.com>
  26. * @since 2.0
  27. */
  28. class BatchQueryResult extends Object implements \Iterator
  29. {
  30. /**
  31. * @var Connection the DB connection to be used when performing batch query.
  32. * If null, the "db" application component will be used.
  33. */
  34. public $db;
  35. /**
  36. * @var Query the query object associated with this batch query.
  37. * Do not modify this property directly unless after [[reset()]] is called explicitly.
  38. */
  39. public $query;
  40. /**
  41. * @var integer the number of rows to be returned in each batch.
  42. */
  43. public $batchSize = 100;
  44. /**
  45. * @var boolean whether to return a single row during each iteration.
  46. * If false, a whole batch of rows will be returned in each iteration.
  47. */
  48. public $each = false;
  49. /**
  50. * @var DataReader the data reader associated with this batch query.
  51. */
  52. private $_dataReader;
  53. /**
  54. * @var array the data retrieved in the current batch
  55. */
  56. private $_batch;
  57. /**
  58. * @var mixed the value for the current iteration
  59. */
  60. private $_value;
  61. /**
  62. * @var string|integer the key for the current iteration
  63. */
  64. private $_key;
  65. /**
  66. * Destructor.
  67. */
  68. public function __destruct()
  69. {
  70. // make sure cursor is closed
  71. $this->reset();
  72. }
  73. /**
  74. * Resets the batch query.
  75. * This method will clean up the existing batch query so that a new batch query can be performed.
  76. */
  77. public function reset()
  78. {
  79. if ($this->_dataReader !== null) {
  80. $this->_dataReader->close();
  81. }
  82. $this->_dataReader = null;
  83. $this->_batch = null;
  84. $this->_value = null;
  85. $this->_key = null;
  86. }
  87. /**
  88. * Resets the iterator to the initial state.
  89. * This method is required by the interface [[\Iterator]].
  90. */
  91. public function rewind()
  92. {
  93. $this->reset();
  94. $this->next();
  95. }
  96. /**
  97. * Moves the internal pointer to the next dataset.
  98. * This method is required by the interface [[\Iterator]].
  99. */
  100. public function next()
  101. {
  102. if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {
  103. $this->_batch = $this->fetchData();
  104. reset($this->_batch);
  105. }
  106. if ($this->each) {
  107. $this->_value = current($this->_batch);
  108. if ($this->query->indexBy !== null) {
  109. $this->_key = key($this->_batch);
  110. } elseif (key($this->_batch) !== null) {
  111. $this->_key++;
  112. } else {
  113. $this->_key = null;
  114. }
  115. } else {
  116. $this->_value = $this->_batch;
  117. $this->_key = $this->_key === null ? 0 : $this->_key + 1;
  118. }
  119. }
  120. /**
  121. * Fetches the next batch of data.
  122. * @return array the data fetched
  123. */
  124. protected function fetchData()
  125. {
  126. if ($this->_dataReader === null) {
  127. $this->_dataReader = $this->query->createCommand($this->db)->query();
  128. }
  129. $rows = [];
  130. $count = 0;
  131. while ($count++ < $this->batchSize && ($row = $this->_dataReader->read())) {
  132. $rows[] = $row;
  133. }
  134. return $this->query->populate($rows);
  135. }
  136. /**
  137. * Returns the index of the current dataset.
  138. * This method is required by the interface [[\Iterator]].
  139. * @return integer the index of the current row.
  140. */
  141. public function key()
  142. {
  143. return $this->_key;
  144. }
  145. /**
  146. * Returns the current dataset.
  147. * This method is required by the interface [[\Iterator]].
  148. * @return mixed the current dataset.
  149. */
  150. public function current()
  151. {
  152. return $this->_value;
  153. }
  154. /**
  155. * Returns whether there is a valid dataset at the current position.
  156. * This method is required by the interface [[\Iterator]].
  157. * @return boolean whether there is a valid dataset at the current position.
  158. */
  159. public function valid()
  160. {
  161. return !empty($this->_batch);
  162. }
  163. }