Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@
- Chg #1058: Refactor `Expression` class: declare `$expression` and `$params` as public readonly properties, remove
`getParams()` method (@vjik)
- New #1062: `Stringable` value support in `Like` and `NotLike` conditions (@vjik)
- New #1063: Add `indexBy()` method to `BatchQueryResultInterface` and `BatchQueryResult` class (@vjik)
- Chg #1063: `AbstractConnection::createBatchQueryResult()` passes parameters `indexBy` and `resultCallback` to
`BatchQueryResult` being created (@vjik)

## 1.3.0 March 21, 2024

Expand Down
1 change: 1 addition & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace
- Change return type of `key()` method to `int` in `BatchQueryResultInterface` and `BatchQueryResult` class;
- Change return type of `current()` method to `array` in `BatchQueryResultInterface` and `BatchQueryResult` class;
- Remove `null` from return type of `getQuery()` method in `BatchQueryResultInterface` and `BatchQueryResult` class;
- Add `indexBy()` method to `BatchQueryResultInterface` and `BatchQueryResult` class;
- Remove parameters from `each()` method in `QueryInterface` and `Query` class;
- Change return type of `each()` method to `DataReaderInterface` in `QueryInterface` and `Query` class;
- Add `$columnFactory` parameter to `AbstractPdoConnection::__construct()` constructor;
Expand Down
4 changes: 3 additions & 1 deletion src/Connection/AbstractConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ public function beginTransaction(?string $isolationLevel = null): TransactionInt

public function createBatchQueryResult(QueryInterface $query): BatchQueryResultInterface
{
return new BatchQueryResult($query);
return (new BatchQueryResult($query))
->indexBy($query->getIndexBy())
->resultCallback($query->getResultCallback());
}

public function createQuery(): QueryInterface
Expand Down
2 changes: 1 addition & 1 deletion src/Helper/DbArrayHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public static function arrange(
*
* @return array[]|object[] The indexed array.
*
* @psalm-param list<array> $rows
* @psalm-param array<array> $rows
* @psalm-param IndexBy|null $indexBy
* @psalm-param ResultCallback|null $resultCallback
*/
Expand Down
26 changes: 22 additions & 4 deletions src/Query/BatchQueryResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
*
* A batch query is a group of many SQL statements that are executed together as a single unit.
*
* @psalm-import-type ResultCallback from BatchQueryResultInterface
* @psalm-import-type IndexBy from QueryInterface
* @psalm-import-type ResultCallback from QueryInterface
*/
final class BatchQueryResult implements BatchQueryResultInterface
{
Expand All @@ -26,6 +27,12 @@ final class BatchQueryResult implements BatchQueryResultInterface
* @var DataReaderInterface|null The data reader associated with this batch query.
*/
private DataReaderInterface|null $dataReader = null;

/**
* @psalm-var IndexBy|null $indexBy
*/
private Closure|string|null $indexBy = null;

/**
* @var Closure|null A callback function to process the result rows.
* @psalm-var ResultCallback|null
Expand Down Expand Up @@ -74,21 +81,26 @@ private function fetchData(): array
/**
* Reads and collects rows for batch.
*
* @psalm-return list<array>
* @psalm-return array<array>
*/
private function getRows(): array
{
$rows = [];

$this->dataReader ??= $this->query->createCommand()->query();
$this->dataReader ??= $this->query->createCommand()->query()->indexBy($this->indexBy);

$isContinuousIndex = $this->indexBy === null;
$startIndex = $isContinuousIndex ? ($this->index + 2) * $this->batchSize : 0;

for (
$leftCount = $this->batchSize;
$leftCount > 0 && $this->dataReader->valid();
--$leftCount, $this->dataReader->next()
) {
/** @var int|string $key */
$key = $isContinuousIndex ? $startIndex - $leftCount : $this->dataReader->key();
/** @var array */
$rows[] = $this->dataReader->current();
$rows[$key] = $this->dataReader->current();
}

return $rows;
Expand Down Expand Up @@ -138,6 +150,12 @@ public function batchSize(int $value): static
return $this;
}

public function indexBy(Closure|string|null $indexBy): static
{
$this->indexBy = $indexBy;
return $this;
}

public function resultCallback(Closure|null $callback): static
{
$this->resultCallback = $callback;
Expand Down
7 changes: 6 additions & 1 deletion src/Query/BatchQueryResultInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
* @extends Iterator<int, array>
*
* @psalm-import-type IndexBy from QueryInterface
* @psalm-type ResultCallback = Closure(non-empty-list<array>):non-empty-array<array|object>
* @psalm-import-type ResultCallback from QueryInterface
*/
interface BatchQueryResultInterface extends Iterator
{
Expand Down Expand Up @@ -98,6 +98,11 @@ public function getBatchSize(): int;
*/
public function batchSize(int $value): static;

/**
* @psalm-param IndexBy|null $indexBy
*/
public function indexBy(Closure|string|null $indexBy): static;

/**
* Sets a callback function to be called for the result of the query.
*
Expand Down
7 changes: 3 additions & 4 deletions src/Query/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,7 @@ public function batch(int $batchSize = 100): BatchQueryResultInterface
{
return $this->db
->createBatchQueryResult($this)
->batchSize($batchSize)
->resultCallback($this->index(...));
->batchSize($batchSize);
}

public function column(): array
Expand Down Expand Up @@ -846,12 +845,12 @@ protected function queryScalar(string|ExpressionInterface $selectExpression): bo
}

/**
* @psalm-param list<array> $rows
* @psalm-param array<array> $rows
*
* @return array[]|object[]
*
* @psalm-return (
* $rows is non-empty-list<array>
* $rows is non-empty-array<array>
* ? non-empty-array<array|object>
* : array[]|object[]
* )
Expand Down
2 changes: 1 addition & 1 deletion src/Query/QueryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @psalm-type IndexBy = Closure(array):array-key|string
* @psalm-import-type ParamsType from ConnectionInterface
* @psalm-import-type SelectValue from QueryPartsInterface
* @psalm-type ResultCallback = Closure(non-empty-list<array>):non-empty-list<array|object>
* @psalm-type ResultCallback = Closure(non-empty-array<array>):non-empty-array<array|object>
* @psalm-type ResultCallbackOne = Closure(array):(array|object)
*/
interface QueryInterface extends ExpressionInterface, QueryPartsInterface, QueryFunctionsInterface
Expand Down
55 changes: 55 additions & 0 deletions tests/Common/CommonQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,59 @@ public function testLikeCaseInsensitive(mixed $expected, string $value): void

$this->assertSame($expected, $result);
}

public function testBatchWithResultCallback(): void
{
$db = $this->getConnection(true);

$batch = (new Query($db))
->select('name')
->from('customer')
->limit(2)
->resultCallback(
static fn(array $rows) => array_map(
static fn(array $row) => $row['name'] . ' (ok)',
$rows,
),
)
->batch(1);

$results = [];
foreach ($batch as $rows) {
$results[] = $rows;
}

$this->assertSame(
[
[0 => 'user1 (ok)'],
[1 => 'user2 (ok)'],
],
$results,
);
}

public function testBatchWithIndexBy(): void
{
$db = $this->getConnection(true);

$batch = (new Query($db))
->select(['name', 'email'])
->from('customer')
->limit(2)
->indexBy('name')
->batch(1);

$results = [];
foreach ($batch as $rows) {
$results[] = array_keys($rows);
}

$this->assertSame(
[
['user1'],
['user2'],
],
$results,
);
}
}
Loading