BatchQueryResult::next()   B
last analyzed

Complexity

Conditions 9
Paths 12

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 9

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 20
ccs 10
cts 10
cp 1
rs 8.0555
c 0
b 0
f 0
cc 9
nc 12
nop 0
crap 9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Query;
6
7
use Closure;
8
use Throwable;
9
use Yiisoft\Db\Exception\Exception;
10
use Yiisoft\Db\Exception\InvalidConfigException;
11
use Yiisoft\Db\Query\Data\DataReaderInterface;
12
13
use function current;
14
use function key;
15
use function next;
16
use function reset;
17
18
/**
19
 * Represents the result of a batch query execution.
20
 *
21
 * A batch query is a group of many SQL statements that are executed together as a single unit.
22
 */
23
class BatchQueryResult implements BatchQueryResultInterface
24
{
25
    protected int $batchSize = 100;
26
    private int|string|null $key = null;
27
28
    /**
29
     * @var DataReaderInterface|null The data reader associated with this batch query.
30
     */
31
    protected DataReaderInterface|null $dataReader = null;
32
33
    /**
34
     * @var array|null The data retrieved in the current batch.
35
     */
36
    private array|null $batch = null;
37
38
    private Closure|null $populateMethod = null;
39
40
    /**
41
     * @var mixed The value for the current iteration.
42
     */
43
    private mixed $value;
44
45
    public function __construct(private QueryInterface $query, private bool $each = false)
46
    {
47
    }
48
49
    public function __destruct()
50
    {
51
        $this->reset();
52
    }
53
54
    public function reset(): void
55
    {
56
        $this->dataReader = null;
57
        $this->batch = null;
58
        $this->value = null;
59
        $this->key = null;
60
    }
61
62
    public function rewind(): void
63
    {
64
        $this->reset();
65
        $this->next();
66
    }
67
68 30
    public function next(): void
69
    {
70 30
        if ($this->batch === null || !$this->each || (next($this->batch) === false)) {
71 30
            $this->batch = $this->fetchData();
72
            reset($this->batch);
73
        }
74
75
        if ($this->each) {
76
            $this->value = current($this->batch);
77
78 30
            if ($this->query->getIndexBy() !== null) {
79
                $this->key = key($this->batch);
80 30
            } elseif (key($this->batch) !== null) {
81 30
                $this->key = $this->key === null ? 0 : (int) $this->key + 1;
82
            } else {
83
                $this->key = null;
84 30
            }
85 30
        } else {
86 30
            $this->value = $this->batch;
87 30
            $this->key = $this->key === null ? 0 : (int) $this->key + 1;
88 30
        }
89
    }
90
91
    /**
92
     * Fetches the next batch of data.
93
     *
94
     * @throws Exception
95 30
     * @throws InvalidConfigException
96
     * @throws Throwable
97 30
     *
98 30
     * @return array The data fetched.
99 30
     */
100
    protected function fetchData(): array
101
    {
102
        if ($this->dataReader === null) {
103
            $this->dataReader = $this->query->createCommand()->query();
104
        }
105
106 30
        $rows = $this->getRows();
107
108 30
        if ($this->populateMethod !== null) {
109 30
            return (array) ($this->populateMethod)($rows, $this->query->getIndexBy());
110 30
        }
111
112
        return $rows;
113 30
    }
114 10
115 10
    /**
116 10
     * Reads and collects rows for batch.
117 10
     */
118 10
    protected function getRows(): array
119
    {
120 10
        $rows = [];
121
        $count = 0;
122
123 30
        do {
124 30
            $this->dataReader?->next();
125
            /** @psalm-var array|bool $row */
126 30
            $row = $this->dataReader?->current();
127
        } while ($row && ($rows[] = $row) && ++$count < $this->batchSize);
128
129
        return $rows;
130
    }
131
132
    public function key(): int|string|null
133
    {
134
        return $this->key;
135 30
    }
136
137 30
    public function current(): mixed
138 30
    {
139
        return $this->value;
140
    }
141 30
142
    public function valid(): bool
143 30
    {
144
        return !empty($this->batch);
145
    }
146
147
    public function getQuery(): QueryInterface|null
148
    {
149
        return $this->query;
150
    }
151 30
152
    public function getBatchSize(): int
153 30
    {
154 30
        return $this->batchSize;
155
    }
156
157 30
    public function batchSize(int $value): self
158 30
    {
159
        $this->batchSize = $value;
160 6
161 6
        return $this;
162 6
    }
163
164
    public function setPopulatedMethod(Closure|null $populateMethod = null): static
165
    {
166
        $this->populateMethod = $populateMethod;
167 30
168
        return $this;
169
    }
170
}
171