BatchQueryResult.php 4.73 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
 * 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
20
 * $query = (new Query)->from('user');
21 22 23
 * 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
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class BatchQueryResult extends Object implements \Iterator
{
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
    /**
     * @var Connection the DB connection to be used when performing batch query.
     * If null, the "db" application component will be used.
     */
    public $db;
    /**
     * @var Query the query object associated with this batch query.
     * Do not modify this property directly unless after [[reset()]] is called explicitly.
     */
    public $query;
    /**
     * @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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    /**
     * @var DataReader the data reader associated with this batch query.
     */
    private $_dataReader;
    /**
     * @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
     */
    private $_key;
69

70

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

80 81 82 83 84 85 86 87 88 89 90 91 92 93
    /**
     * Resets the batch query.
     * This method will clean up the existing batch query so that a new batch query can be performed.
     */
    public function reset()
    {
        if ($this->_dataReader !== null) {
            $this->_dataReader->close();
        }
        $this->_dataReader = null;
        $this->_batch = null;
        $this->_value = null;
        $this->_key = null;
    }
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();
    }
104

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

116 117 118 119 120 121 122 123 124 125 126 127 128 129
        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;
        }
    }
130

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

141 142 143 144 145
        $rows = [];
        $count = 0;
        while ($count++ < $this->batchSize && ($row = $this->_dataReader->read())) {
            $rows[] = $row;
        }
Qiang Xue committed
146

147
        return $this->query->populate($rows);
148
    }
Qiang Xue committed
149

150 151 152 153 154 155 156 157 158
    /**
     * 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;
    }
Qiang Xue committed
159

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

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