Skip to content

Commit

Permalink
Merge pull request #40 from TomHAnderson/feature/pageinfo
Browse files Browse the repository at this point in the history
startCursor and hasPreviousPage added to PageInfo
  • Loading branch information
TomHAnderson authored Aug 4, 2022
2 parents cae3dc3 + 44da907 commit 899e569
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 31 deletions.
4 changes: 3 additions & 1 deletion src/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ static function (ContainerInterface $container) {
)
->set(
Connection::class,
static fn () => new Connection()
static function (ContainerInterface $container) {
return new Connection($container->get(TypeManager::class));
}
);
}

Expand Down
20 changes: 14 additions & 6 deletions src/Resolve/ResolveCollectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public function get(Entity $entity): Closure
$itemCount = count($collection->matching($criteria));

if ($last && ! $before) {
$offset = $itemCount - $last - 1;
$offset = $itemCount - $last;
}

if ($offset) {
Expand All @@ -183,9 +183,10 @@ public function get(Entity $entity): Closure
// Fetch slice of collection
$items = $collection->matching($criteria);

$edges = [];
$index = 0;
$lastCursor = base64_encode((string) 0);
$edges = [];
$index = 0;
$lastCursor = base64_encode((string) 0);
$firstCursor = null;
foreach ($items as $result) {
$cursor = base64_encode((string) ($index + $offset));

Expand All @@ -195,19 +196,26 @@ public function get(Entity $entity): Closure
];

$lastCursor = $cursor;
if (! $firstCursor) {
$firstCursor = $cursor;
}

$index++;
}

$endCursor = $itemCount ? $itemCount - 1 : 0;
$endCursor = base64_encode((string) $endCursor);
$endCursor = $itemCount ? $itemCount - 1 : 0;
$startCursor = base64_encode((string) 0);
$endCursor = base64_encode((string) $endCursor);

// Return entities
return [
'edges' => $edges,
'totalCount' => $itemCount,
'pageInfo' => [
'endCursor' => $endCursor,
'startCursor' => $startCursor,
'hasNextPage' => $endCursor !== $lastCursor,
'hasPreviousPage' => $firstCursor !== null && $startCursor !== $firstCursor,
],
];
};
Expand Down
20 changes: 14 additions & 6 deletions src/Resolve/ResolveEntityFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,15 @@ public function get(Entity $entity): Closure
$itemCount = $paginator->count();

if ($last && ! $before) {
$offset = $itemCount - $last - 1;
$offset = $itemCount - $last;
$queryBuilder->setFirstResult($offset);
$paginator = new Paginator($queryBuilder->getQuery());
}

$edges = [];
$index = 0;
$lastCursor = base64_encode((string) 0);
$edges = [];
$index = 0;
$lastCursor = base64_encode((string) 0);
$firstCursor = null;
foreach ($paginator->getQuery()->getResult() as $result) {
$cursor = base64_encode((string) ($index + $offset));

Expand All @@ -168,18 +169,25 @@ public function get(Entity $entity): Closure
];

$lastCursor = $cursor;
if (! $firstCursor) {
$firstCursor = $cursor;
}

$index++;
}

$endCursor = $paginator->count() ? $paginator->count() - 1 : 0;
$endCursor = base64_encode((string) $endCursor);
$endCursor = $paginator->count() ? $paginator->count() - 1 : 0;
$startCursor = base64_encode((string) 0);
$endCursor = base64_encode((string) $endCursor);

return [
'edges' => $edges,
'totalCount' => $paginator->count(),
'pageInfo' => [
'endCursor' => $endCursor,
'startCursor' => $startCursor,
'hasNextPage' => $endCursor !== $lastCursor,
'hasPreviousPage' => $firstCursor !== null && $startCursor !== $firstCursor,
],
];
};
Expand Down
21 changes: 6 additions & 15 deletions src/Type/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

class Connection
{
public function __construct(
private TypeManager $typeManager
) {
}

public function get(ObjectType $objectType, ?string $objectName = null): ObjectType
{
$objectName ??= $objectType->name;
Expand All @@ -19,7 +24,7 @@ public function get(ObjectType $objectType, ?string $objectName = null): ObjectT
'fields' => [
'edges' => Type::listOf($this->getNodeObjectType($objectType, $objectName)),
'totalCount' => Type::nonNull(Type::int()),
'pageInfo' => $this->getPageInfo($objectName),
'pageInfo' => $this->typeManager->get('PageInfo'),
],
];

Expand All @@ -38,18 +43,4 @@ private function getNodeObjectType(ObjectType $objectType, string $objectName):

return new ObjectType($configuration);
}

private function getPageInfo(string $typeName): ObjectType
{
$configuration = [
'name' => $typeName . '_PageInfo',
'description' => 'Page information',
'fields' => [
'endCursor' => Type::nonNull(Type::string()),
'hasNextPage' => Type::nonNull(Type::boolean()),
],
];

return new ObjectType($configuration);
}
}
27 changes: 27 additions & 0 deletions src/Type/PageInfo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace ApiSkeletons\Doctrine\GraphQL\Type;

use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

class PageInfo extends ObjectType
{
public function __construct()
{
$configuration = [
'name' => 'PageInfo',
'description' => 'Page information',
'fields' => [
'startCursor' => Type::nonNull(Type::string()),
'endCursor' => Type::nonNull(Type::string()),
'hasPreviousPage' => Type::nonNull(Type::boolean()),
'hasNextPage' => Type::nonNull(Type::boolean()),
],
];

parent::__construct($configuration);
}
}
5 changes: 4 additions & 1 deletion src/Type/TypeManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class TypeManager extends AbstractContainer
{
public function __construct()
{
$pageInfo = new PageInfo();

$this
->set('tinyint', static fn () => Type::int())
->set('smallint', static fn () => Type::int())
Expand All @@ -24,6 +26,7 @@ public function __construct()
->set('string', static fn () => Type::string())
->set('text', static fn () => Type::string())
->set('array', static fn () => Type::listOf(Type::string()))
->set('datetime', static fn () => new DateTimeType());
->set('datetime', static fn () => new DateTimeType())
->set('PageInfo', static fn () => new PageInfo());
}
}
2 changes: 1 addition & 1 deletion test/Feature/Resolve/CollectionFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public function testlast(): void
$data = $result->toArray()['data'];

$this->assertEquals(3, count($data['artist']['edges'][0]['node']['performances']['edges']));
$this->assertEquals(2, $data['artist']['edges'][0]['node']['performances']['edges'][0]['node']['id']);
$this->assertEquals(3, $data['artist']['edges'][0]['node']['performances']['edges'][0]['node']['id']);
}

public function testlastbefore(): void
Expand Down
2 changes: 1 addition & 1 deletion test/Feature/Resolve/EntityFilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ public function testlast(Schema $schema): void
$data = $result->toArray()['data'];

$this->assertEquals(3, count($data['performance']['edges']));
$this->assertEquals(5, $data['performance']['edges'][0]['node']['id']);
$this->assertEquals(6, $data['performance']['edges'][0]['node']['id']);
}

/**
Expand Down
145 changes: 145 additions & 0 deletions test/Feature/Type/PageInfoTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

namespace ApiSkeletonsTest\Doctrine\GraphQL\Feature\Resolve;

use ApiSkeletons\Doctrine\GraphQL\Config;
use ApiSkeletons\Doctrine\GraphQL\Driver;
use ApiSkeletonsTest\Doctrine\GraphQL\AbstractTest;
use ApiSkeletonsTest\Doctrine\GraphQL\Entity\Performance;
use GraphQL\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;

use function count;

class PageInfoTest extends AbstractTest
{
public function testPageInfo(): void
{
$driver = new Driver($this->getEntityManager());
$schema = new Schema([
'query' => new ObjectType([
'name' => 'query',
'fields' => [
'performance' => [
'type' => $driver->connection($driver->type(Performance::class)),
'args' => [
'filter' => $driver->filter(Performance::class),
],
'resolve' => $driver->resolve(Performance::class),
],
],
]),
]);

$query = '{
performance {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
}
}
}
}';
$result = GraphQL::executeQuery($schema, $query);

$data = $result->toArray()['data'];

$this->assertFalse($data['performance']['pageInfo']['hasNextPage']);
$this->assertFalse($data['performance']['pageInfo']['hasPreviousPage']);
$this->assertEquals(
$data['performance']['edges'][0]['cursor'],
$data['performance']['pageInfo']['startCursor']
);
$this->assertEquals(
$data['performance']['edges'][count($data['performance']['edges']) - 1]['cursor'],
$data['performance']['pageInfo']['endCursor']
);
}

public function testPageInfoHasNextPage(): void
{
$driver = new Driver($this->getEntityManager());
$schema = new Schema([
'query' => new ObjectType([
'name' => 'query',
'fields' => [
'performance' => [
'type' => $driver->connection($driver->type(Performance::class)),
'args' => [
'filter' => $driver->filter(Performance::class),
],
'resolve' => $driver->resolve(Performance::class),
],
],
]),
]);

$query = '{
performance (filter: {_first:2}) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
}
}
}
}';
$result = GraphQL::executeQuery($schema, $query);

$data = $result->toArray()['data'];

$this->assertTrue($data['performance']['pageInfo']['hasNextPage']);
$this->assertFalse($data['performance']['pageInfo']['hasPreviousPage']);
}

public function testPageInfoHasPreviousPage(): void
{
$driver = new Driver($this->getEntityManager());
$schema = new Schema([
'query' => new ObjectType([
'name' => 'query',
'fields' => [
'performance' => [
'type' => $driver->connection($driver->type(Performance::class)),
'args' => [
'filter' => $driver->filter(Performance::class),
],
'resolve' => $driver->resolve(Performance::class),
],
],
]),
]);

$query = '{
performance (filter: {_last:2}) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
}
}
}
}';
$result = GraphQL::executeQuery($schema, $query);

$data = $result->toArray()['data'];

$this->assertFalse($data['performance']['pageInfo']['hasNextPage']);
$this->assertTrue($data['performance']['pageInfo']['hasPreviousPage']);
}
}

0 comments on commit 899e569

Please sign in to comment.