BatchQueryResult.php 4.15 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\db;

use yii\base\Object;

/**
13 14 15 16 17 18 19 20 21 22 23
 * BatchQueryResult represents a batch query from which you can retrieve data in batches.
 *
 * You usually do not instantiate BatchQueryResult directly. Instead, you obtain it by
 * calling [[Query::batch()]] or [[Query::each()]]. Because BatchQueryResult implements the `Iterator` interface,
 * you can iterate it to obtain a batch of data in each iteration. For example,
 *
 * ```php
 * $query = (new Query)->from('tbl_user');
 * foreach ($query->batch() as $i => $users) {
 *     // $users represents the rows in the $i-th batch
 * }
Qiang Xue committed
24 25
 * foreach ($query->each() as $user) {
 * }
26
 * ```
27 28 29 30 31 32 33
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class BatchQueryResult extends Object implements \Iterator
{
	/**
34 35
	 * @var Connection the DB connection to be used when performing batch query.
	 * If null, the "db" application component will be used.
36 37 38
	 */
	public $db;
	/**
39 40
	 * @var Query the query object associated with this batch query.
	 * Do not modify this property directly unless after [[reset()]] is called explicitly.
41 42
	 */
	public $query;
Qiang Xue committed
43 44 45 46 47 48 49 50 51
	/**
	 * @var integer the number of rows to be returned in each batch.
	 */
	public $batchSize = 100;
	/**
	 * @var boolean whether to return a single row during each iteration.
	 * If false, a whole batch of rows will be returned in each iteration.
	 */
	public $each = false;
52
	/**
53
	 * @var DataReader the data reader associated with this batch query.
54
	 */
Qiang Xue committed
55
	private $_dataReader;
56
	/**
Qiang Xue committed
57 58 59 60 61 62 63 64 65
	 * @var array the data retrieved in the current batch
	 */
	private $_batch;
	/**
	 * @var mixed the value for the current iteration
	 */
	private $_value;
	/**
	 * @var string|integer the key for the current iteration
66
	 */
67
	private $_key;
68

69 70 71
	/**
	 * Destructor.
	 */
72 73
	public function __destruct()
	{
74
		// make sure cursor is closed
75 76 77
		$this->reset();
	}

78 79 80 81
	/**
	 * Resets the batch query.
	 * This method will clean up the existing batch query so that a new batch query can be performed.
	 */
82 83
	public function reset()
	{
Qiang Xue committed
84 85
		if ($this->_dataReader !== null) {
			$this->_dataReader->close();
86
		}
Qiang Xue committed
87 88 89 90
		$this->_dataReader = null;
		$this->_batch = null;
		$this->_value = null;
		$this->_key = null;
91 92 93 94 95 96 97 98 99 100 101 102 103
	}

	/**
	 * Resets the iterator to the initial state.
	 * This method is required by the interface Iterator.
	 */
	public function rewind()
	{
		$this->reset();
		$this->next();
	}

	/**
Qiang Xue committed
104
	 * Moves the internal pointer to the next dataset.
105 106
	 * This method is required by the interface Iterator.
	 */
Qiang Xue committed
107
	public function next()
108
	{
Qiang Xue committed
109 110 111
		if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {
			$this->_batch = $this->fetchData();
		}
112

Qiang Xue committed
113 114 115 116 117 118 119 120 121 122 123 124 125
		if ($this->each) {
			$this->_value = current($this->_batch);
			if ($this->query->indexBy !== null) {
				$this->_key = key($this->_batch);
			} elseif (key($this->_batch) !== null) {
				$this->_key++;
			} else {
				$this->_key = null;
			}
		} else {
			$this->_value = $this->_batch;
			$this->_key = $this->_key === null ? 0 : $this->_key + 1;
		}
126 127 128
	}

	/**
Qiang Xue committed
129 130
	 * Fetches the next batch of data.
	 * @return array the data fetched
131
	 */
Qiang Xue committed
132
	protected function fetchData()
133
	{
Qiang Xue committed
134 135
		if ($this->_dataReader === null) {
			$this->_dataReader = $this->query->createCommand($this->db)->query();
136 137 138 139
		}

		$rows = [];
		$count = 0;
Qiang Xue committed
140
		while ($count++ < $this->batchSize && ($row = $this->_dataReader->read())) {
141 142
			$rows[] = $row;
		}
Qiang Xue committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

		return $this->query->prepareResult($rows);
	}

	/**
	 * Returns the index of the current dataset.
	 * This method is required by the interface Iterator.
	 * @return integer the index of the current row.
	 */
	public function key()
	{
		return $this->_key;
	}

	/**
	 * Returns the current dataset.
	 * This method is required by the interface Iterator.
	 * @return mixed the current dataset.
	 */
	public function current()
	{
		return $this->_value;
165 166 167
	}

	/**
168
	 * Returns whether there is a valid dataset at the current position.
169
	 * This method is required by the interface Iterator.
170
	 * @return boolean whether there is a valid dataset at the current position.
171 172 173
	 */
	public function valid()
	{
Qiang Xue committed
174
		return !empty($this->_batch);
175 176
	}
}