Skip to content
Open
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
12 changes: 12 additions & 0 deletions src/Exception/InvalidPerPageException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Ray\MediaQuery\Exception;

/**
* PerPage must be a positive integer
*/
final class InvalidPerPageException extends LogicException
{
}
6 changes: 6 additions & 0 deletions src/MappedPages.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ public function count(): int
{
return $this->pages->count();
}

#[Override]
public function getNbPages(): int
{
return $this->pages->getNbPages();
}
}
23 changes: 22 additions & 1 deletion src/Pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@
use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerInterface;
use Ray\AuraSqlModule\Pagerfanta\ExtendedPdoAdapter;
use Ray\AuraSqlModule\Pagerfanta\Page;
use Ray\MediaQuery\Exception\InvalidPerPageException;
use Ray\MediaQuery\Exception\LogicException;

use function ceil;
use function is_array;
use function max;

/** @template T of class-string|mixed */
final class Pages implements PagesInterface
{
/** @var (callable(array<array-key, mixed>): mixed)|null */
private $rowMapper;

/**
* Memoized result count, shared by count() and getNbPages() as in Pagerfanta
*
* @var int<0, max>|null
*/
private int|null $nbResults = null;

/**
* @param array<string, mixed> $params
* @param (callable(array<array-key, mixed>): mixed)|null $rowMapper
Expand All @@ -28,8 +38,13 @@ public function __construct(
private ExtendedPdoInterface $pdo,
private string $sql,
private array $params,
private int $perPage,
callable|null $rowMapper = null,
) {
if ($this->perPage < 1) {
throw new InvalidPerPageException((string) $this->perPage);
}

$this->rowMapper = $rowMapper;
}

Expand Down Expand Up @@ -93,6 +108,12 @@ public function offsetUnset(mixed $offset): void
#[Override]
public function count(): int
{
return (new ExtendedPdoAdapter($this->pdo, $this->sql, $this->params))->getNbResults();
return $this->nbResults ??= (new ExtendedPdoAdapter($this->pdo, $this->sql, $this->params))->getNbResults();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

#[Override]
public function getNbPages(): int
{
return max(1, (int) ceil($this->count() / $this->perPage));
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
4 changes: 4 additions & 0 deletions src/PagesInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@
/** @extends ArrayAccess<int, mixed> */
interface PagesInterface extends ArrayAccess, Countable
{
/**
* Returns the total number of pages (ceil of result count / perPage, minimum 1 as in Pagerfanta).
*/
public function getNbPages(): int;
}
2 changes: 1 addition & 1 deletion src/SqlQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public function getPages(string $sqlId, array $values, int $perPage, string $que
$pager = $this->pagerFactory->newInstance($this->pdo, $this->getSql($sqlId), $values, $perPage, $queryTemplate, $entity);

/** @var array<string, mixed> $values */
return new Pages($pager, $this->pdo, $this->getSql($sqlId), $values);
return new Pages($pager, $this->pdo, $this->getSql($sqlId), $values, $perPage);
}

private function getSql(string $sqlId): string
Expand Down
6 changes: 6 additions & 0 deletions tests/Fake/FakeEmptyPages.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public function count(): int
{
return $this->iter->count();
}

public function getNbPages(): int
{
// Empty result set is still one (empty) page, as in Pagerfanta
return 1;
}
}
6 changes: 6 additions & 0 deletions tests/Fake/FakePages.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ public function count(): int
{
// TODO: Implement count() method.
}

public function getNbPages(): int
{
// TODO: Implement getNbPages() method.
return 0;
}
}
5 changes: 5 additions & 0 deletions tests/Fake/MappedPagesFakePages.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ public function count(): int
{
return 1;
}

public function getNbPages(): int
{
return 1;
}
}
11 changes: 11 additions & 0 deletions tests/MappedPagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ public function testDelegatesMutationAndCount(): void
$this->assertSame(1, $pages->count());
}

public function testDelegatesGetNbPages(): void
{
$delegate = $this->pages($this->page([]));
$pages = new MappedPages(
$delegate,
static fn (array $row): array => $row,
);

$this->assertSame(1, $pages->getNbPages());
}

private function page(mixed $data): Page
{
$reflection = new ReflectionClass(Page::class);
Expand Down
63 changes: 63 additions & 0 deletions tests/PagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Aura\Sql\ExtendedPdoInterface;
use PHPUnit\Framework\TestCase;
use Ray\AuraSqlModule\Pagerfanta\AuraSqlPagerInterface;
use Ray\MediaQuery\Exception\InvalidPerPageException;

final class PagesTest extends TestCase
{
Expand All @@ -20,9 +21,71 @@ public function testOffsetGetReturnsNullWhenDelegateHasNoPage(): void
$this->createStub(ExtendedPdoInterface::class),
'SELECT 1',
[],
10,
);

$this->assertFalse(isset($pages[3]));
$this->assertNull($pages[3]);
}

public function testConstructorRejectsZeroPerPage(): void
{
$this->expectException(InvalidPerPageException::class);

new Pages(
$this->createStub(AuraSqlPagerInterface::class),
$this->createStub(ExtendedPdoInterface::class),
'SELECT 1',
[],
0,
);
}

public function testConstructorRejectsNegativePerPage(): void
{
$this->expectException(InvalidPerPageException::class);

new Pages(
$this->createStub(AuraSqlPagerInterface::class),
$this->createStub(ExtendedPdoInterface::class),
'SELECT 1',
[],
-1,
);
}

public function testGetNbPagesReturnsOneWhenEmpty(): void
{
$pdo = $this->createStub(ExtendedPdoInterface::class);
$pdo->method('fetchValue')->willReturn('0');

$pages = new Pages(
$this->createStub(AuraSqlPagerInterface::class),
$pdo,
'SELECT * FROM todo',
[],
10,
);

$this->assertSame(0, $pages->count());
$this->assertSame(1, $pages->getNbPages());
}

public function testCountQueryRunsOnlyOnce(): void
{
$pdo = $this->createMock(ExtendedPdoInterface::class);
$pdo->expects($this->once())->method('fetchValue')->willReturn('3');

$pages = new Pages(
$this->createStub(AuraSqlPagerInterface::class),
$pdo,
'SELECT * FROM todo',
[],
2,
);

$this->assertSame(3, $pages->count());
$this->assertSame(2, $pages->getNbPages());
$this->assertSame(2, $pages->getNbPages());
}
}
32 changes: 32 additions & 0 deletions tests/SqlQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,38 @@ public function testPagerCount(Pages $pages): void
$this->assertSame(2, count($pages));
}

/** @param Pages<mixed> $pages */
#[Depends('testPager')]
public function testPagerNbPages(Pages $pages): void
{
$this->assertSame(2, $pages->getNbPages());
}

public function testPagerNbPagesWithPerPage2(): void
{
$this->sqlQuery->exec('todo_add', ['id' => '2', 'title' => 'walk']);
$pages = $this->sqlQuery->getPages('todo_list', [], 2);

$this->assertSame(1, $pages->getNbPages());
}

public function testPagerNbPagesRoundsUp(): void
{
$this->sqlQuery->exec('todo_add', ['id' => '2', 'title' => 'walk']);
$this->sqlQuery->exec('todo_add', ['id' => '3', 'title' => 'sleep']);
$pages = $this->sqlQuery->getPages('todo_list', [], 2);

$this->assertSame(2, $pages->getNbPages());
}

public function testPagerNbPagesMinimumOneWhenEmpty(): void
{
$pages = $this->sqlQuery->getPages('todo_item', ['id' => '__none__'], 10);

$this->assertSame(0, count($pages));
$this->assertSame(1, $pages->getNbPages());
}

public function testCount(): void
{
$this->sqlQuery->exec('todo_add', ['id' => '2', 'title' => 'walk']);
Expand Down
Loading