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 @@ -5,6 +5,9 @@ Latest
* [#26](https://github.com/cleverage/doctrine-process-bundle/issues/26) Update quality stack: use Rector `withComposerBased()` sets (removed `SYMFONY_64` / `PHPUNIT_100` sets), declare used Symfony packages and PHPUnit range in composer.json, apply quality tools fixes
* [#28](https://github.com/cleverage/doctrine-process-bundle/issues/28) Add missing documentations: complete reference pages for every Task (inherited options, iterable/flushable behaviours, examples, notes), Database to CSV export and CSV to entities import cookbooks. Harmonize index and task template, fix existing documentation.

### Fixes
* [#30](https://github.com/cleverage/doctrine-process-bundle/issues/30) DatabaseReaderTask no longer drops the row following each full page with the `paginate` option

v3.0
------

Expand Down
1 change: 0 additions & 1 deletion docs/reference/tasks/database_reader_task.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,3 @@ Notes
`ids: !php/enum Doctrine\DBAL\ArrayParameterType::INTEGER` with Doctrine DBAL 4.
* The task is designed to be executed once per process run (typically as the entry point): if it receives a new input
after having iterated over all the rows, that input only resets the task, which is skipped.
* With `paginate`, the row following each full page is currently not output.
7 changes: 3 additions & 4 deletions src/Task/Database/DatabaseReaderTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,10 @@ public function execute(ProcessState $state): void
}

if (null !== $options['paginate']) {
$results = [];
$i = 0;
while (false !== $result && $i++ < $options['paginate']) {
$results = [$result];
// Stop fetching as soon as the page is full: a row fetched beyond it would be lost
while (\count($results) < $options['paginate'] && false !== ($result = $this->statement->fetchAssociative())) {
$results[] = $result;
$result = $this->statement->fetchAssociative();
}
$state->setOutput($results);
} else {
Expand Down
62 changes: 62 additions & 0 deletions tests/Task/Database/DatabaseReaderTaskTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,68 @@ protected function getConnection(?ProcessState $state = null): Connection
$task->execute($state);
}

public function testExecuteWithPaginationDoesNotLoseRows(): void
{
$state = $this->createStub(ProcessState::class);
$options = [
'table' => 'my_table',
'sql' => 'SELECT * FROM my_table',
'limit' => null,
'offset' => null,
'paginate' => 2,
'input_as_params' => false,
'params' => [],
'types' => [],
'empty_log_level' => LogLevel::WARNING,
'connection' => null,
];

$resultData1 = ['id' => 1, 'name' => 'test1'];
$resultData2 = ['id' => 2, 'name' => 'test2'];
$resultData3 = ['id' => 3, 'name' => 'test3'];
$result = $this->createStub(Result::class);
$result->method('fetchAssociative')->willReturnOnConsecutiveCalls($resultData1, $resultData2, $resultData3, false, false);

$connection = $this->createStub(Connection::class);
$connection->method('executeQuery')->willReturn($result);

$task = new class($this->logger, $this->doctrine, $options, $connection) extends DatabaseReaderTask {
/**
* @param array<string, mixed> $testOptions
*/
public function __construct(LoggerInterface $logger, ManagerRegistry $doctrine, private readonly array $testOptions, private readonly Connection $testConnection)
{
parent::__construct($logger, $doctrine);
}

/**
* @return array<string, mixed>
*/
protected function getOptions(?ProcessState $state = null): array
{
return $this->testOptions;
}

protected function getConnection(?ProcessState $state = null): Connection
{
return $this->testConnection;
}
};

$outputs = [];
$state->method('setOutput')->willReturnCallback(static function (mixed $output) use (&$outputs): void {
$outputs[] = $output;
});

$task->initialize($state);
$task->execute($state);
self::assertTrue($task->next($state));
$task->execute($state);
self::assertFalse($task->next($state));

self::assertSame([[$resultData1, $resultData2], [$resultData3]], $outputs);
}

public function testFinalize(): void
{
$task = new DatabaseReaderTask($this->logger, $this->doctrine);
Expand Down
Loading