Skip to content

Total count cache #855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
20 changes: 20 additions & 0 deletions docs/helpers/relay-paginator.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,26 @@ class Greetings implements QueryInterface
}
````

#### Total count caching

You may want to cache your total count for paginator result

````php
<?php

use Overblog\GraphQLBundle\Relay\Connection\Paginator;
use Overblog\GraphQLBundle\Relay\Connection\TotalCountCache;

/** @var Paginator $paginator */
return $paginator->auto(
$args,
new TotalCountCache (
fn () => $backend->countAll(),
),
);
````


#### Promise handling

Paginator also supports promises if you [use that feature](https://github.com/webonyx/graphql-php/pull/67)
Expand Down
13 changes: 3 additions & 10 deletions src/Relay/Connection/Paginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class Paginator
public const MODE_PROMISE = true;

private bool $promise;
private int $totalCount;
private ConnectionBuilder $connectionBuilder;

/** @var callable */
Expand Down Expand Up @@ -127,16 +126,10 @@ private function handleEntities($entities, callable $callback)
/**
* @param int|callable $total
*
* @return int|mixed
* @return int
*/
private function computeTotalCount($total, array $callableArgs = [])
private function computeTotalCount($total, array $callableArgs = []): int
{
if (isset($this->totalCount)) {
return $this->totalCount;
}

$this->totalCount = is_callable($total) ? call_user_func_array($total, $callableArgs) : $total;

return $this->totalCount;
return is_callable($total) ? call_user_func_array($total, $callableArgs) : $total;
}
}
40 changes: 40 additions & 0 deletions src/Relay/Connection/TotalCountCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Overblog\GraphQLBundle\Relay\Connection;

use Symfony\Contracts\Service\ResetInterface;

class TotalCountCache implements ResetInterface
{
/**
* @var int|callable
*/
private $total;
private int $totalCount;

/**
* @param int|callable $total
*/
public function __construct($total)
{
$this->total = $total;
}

public function __invoke(array $callableArgs = []): int
{
if (isset($this->totalCount)) {
return $this->totalCount;
}

$this->totalCount = is_callable($this->total) ? call_user_func_array($this->total, $callableArgs) : $this->total;

return $this->totalCount;
}

public function reset(): void
{
unset($this->totalCount);
}
}
25 changes: 25 additions & 0 deletions tests/Relay/Connection/PaginatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Overblog\GraphQLBundle\Relay\Connection\ConnectionInterface;
use Overblog\GraphQLBundle\Relay\Connection\Output\Connection;
use Overblog\GraphQLBundle\Relay\Connection\Paginator;
use Overblog\GraphQLBundle\Relay\Connection\TotalCountCache;
use PHPUnit\Framework\TestCase;
use function array_slice;
use function base64_encode;
Expand Down Expand Up @@ -259,6 +260,30 @@ public function testAutoBackwardWithCallable(): void
$this->assertTrue($result->getPageInfo()->getHasPreviousPage());
}

public function testAutoBackwardWithCachedCallable(): void
{
$paginator = new Paginator(function ($offset, $limit) {
$this->assertSame(1, $offset);
$this->assertSame(4, $limit);

return $this->getData($offset);
});

$countCalled = false;

/** @var Connection $result */
$result = $paginator->auto(new Argument(['last' => 4]), new TotalCountCache(function () use (&$countCalled) {
$countCalled = true;

return 5;
}));

$this->assertTrue($countCalled);
$this->assertCount(4, $result->getEdges());
$this->assertSameEdgeNodeValue(['B', 'C', 'D', 'E'], $result);
$this->assertTrue($result->getPageInfo()->getHasPreviousPage());
}

public function testTotalCallableWithArguments(): void
{
$paginatorBackend = new PaginatorBackend();
Expand Down
47 changes: 47 additions & 0 deletions tests/Relay/Connection/TotalCountCacheTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Overblog\GraphQLBundle\Tests\Relay\Connection;

use Overblog\GraphQLBundle\Relay\Connection\TotalCountCache;
use PHPUnit\Framework\TestCase;

class TotalCountCacheTest extends TestCase
{
public function testCacheCallable(): void
{
$total = $this->createMock(TotalCountCache::class);
$total
->expects($this->once())
->method('__invoke')
->willReturn(7);

$cache = new TotalCountCache($total);

$this->assertEquals(7, $cache());
$this->assertEquals(7, $cache());
}

public function testCacheInt(): void
{
$cache = new TotalCountCache(12);

$this->assertEquals(12, $cache());
}

public function testReset(): void
{
$total = $this->createMock(TotalCountCache::class);
$total
->expects($this->exactly(2))
->method('__invoke')
->willReturn(7);

$cache = new TotalCountCache($total);

$this->assertEquals(7, $cache());
$cache->reset();
$this->assertEquals(7, $cache());
}
}