diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e26494..38a3b4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ value meaning `return nothing` (@samdark) - Chg #163: Rename `FilterableDataInterface::withFilterHandlers()` to `FilterableDataInterface::withAddedFilterHandlers()` (@samdark) - Enh #190: Use `str_contains` for case-sensitive match in `LikeHandler` (@samdark) +- Bug #195: Fix invalid count in `IterableDataReader` when limit or/and offset used (@vjik) ## 1.0.1 January 25, 2023 diff --git a/src/Reader/Iterable/IterableDataReader.php b/src/Reader/Iterable/IterableDataReader.php index 7a8491d..e5e49a6 100644 --- a/src/Reader/Iterable/IterableDataReader.php +++ b/src/Reader/Iterable/IterableDataReader.php @@ -57,7 +57,7 @@ final class IterableDataReader implements DataReaderInterface /** * @psalm-var array */ - private array $iterableFilterHandlers = []; + private array $iterableFilterHandlers; /** * @param iterable $data Data to iterate. @@ -153,13 +153,34 @@ public function getSort(): ?Sort public function count(): int { - return count($this->read()); + return count($this->internalRead(useLimitAndOffset: false)); } /** * @psalm-return array */ public function read(): array + { + return $this->internalRead(useLimitAndOffset: true); + } + + public function readOne(): array|object|null + { + if ($this->limit === 0) { + return null; + } + + /** @infection-ignore-all Any value more than one in `withLimit()` will be ignored because returned `current()` */ + return $this + ->withLimit(1) + ->getIterator() + ->current(); + } + + /** + * @psalm-return array + */ + private function internalRead(bool $useLimitAndOffset): array { $data = []; $skipped = 0; @@ -167,13 +188,13 @@ public function read(): array foreach ($sortedData as $key => $item) { // Don't return more than limit items. - if ($this->limit > 0 && count($data) === $this->limit) { + if ($useLimitAndOffset && $this->limit > 0 && count($data) === $this->limit) { /** @infection-ignore-all Here continue === break */ break; } // Skip offset items. - if ($skipped < $this->offset) { + if ($useLimitAndOffset && $skipped < $this->offset) { ++$skipped; continue; } @@ -187,19 +208,6 @@ public function read(): array return $data; } - public function readOne(): array|object|null - { - if ($this->limit === 0) { - return null; - } - - /** @infection-ignore-all Any value more one in `withLimit()` will be ignored because returned `current()` */ - return $this - ->withLimit(1) - ->getIterator() - ->current(); - } - /** * Return whether an item matches iterable filter. * diff --git a/tests/Reader/Iterable/IterableDataReaderTest.php b/tests/Reader/Iterable/IterableDataReaderTest.php index 3b9b911..95978f8 100644 --- a/tests/Reader/Iterable/IterableDataReaderTest.php +++ b/tests/Reader/Iterable/IterableDataReaderTest.php @@ -169,6 +169,20 @@ public function testCounting(): void $this->assertCount(5, $reader); } + public function testCountWithLimit(): void + { + $reader = (new IterableDataReader(self::DEFAULT_DATASET))->withLimit(2); + $this->assertSame(5, $reader->count()); + $this->assertCount(5, $reader); + } + + public function testCountWithOffset(): void + { + $reader = (new IterableDataReader(self::DEFAULT_DATASET))->withOffset(3); + $this->assertSame(5, $reader->count()); + $this->assertCount(5, $reader); + } + public function testReadOne(): void { $data = self::DEFAULT_DATASET;