Last active
April 18, 2018 21:09
-
-
Save crisu83/71584fdf72f1206211a93b7fdf3336a3 to your computer and use it in GitHub Desktop.
WIP Relay concept for our GraphQL implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ALehdet\AspaApi\GraphQL\Relay; | |
use Digia\GraphQL\Relay\PageInfo; | |
use Digia\GraphQL\Relay\PageInfoBuilderInterface; | |
abstract class AbstractPageBuilder implements PageInfoBuilderInterface | |
{ | |
/** | |
* @inheritdoc | |
*/ | |
public function build(): PageInfo | |
{ | |
$startCursor = $this->getStartCursor(); | |
$endCursor = $this->getEndCursor(); | |
$hasPreviousPage = $this->hasPreviousPage(); | |
$hasNextPage = $this->hasNextPage(); | |
return new PageInfo($startCursor, $endCursor, $hasPreviousPage, $hasNextPage); | |
} | |
/** | |
* @return null|string | |
*/ | |
public function getStartCursor(): ?string | |
{ | |
$firstEdge = $this->getFirstEdge(); | |
return null !== $firstEdge ? $firstEdge->getCursor() : null; | |
} | |
/** | |
* @return null|string | |
*/ | |
public function getEndCursor(): ?string | |
{ | |
$lastEdge = $this->getLastEdge(); | |
return null !== $lastEdge ? $lastEdge->getCursor() : null; | |
} | |
/** | |
* @return bool | |
*/ | |
public function hasPreviousPage(): bool | |
{ | |
return false; // not supported | |
} | |
/** | |
* @return bool | |
*/ | |
public function hasNextPage(): bool | |
{ | |
return false; // not supported | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
class ArrayConnection implements ConnectionInterface | |
{ | |
/** | |
* @var array | |
*/ | |
protected $data; | |
/** | |
* @var ConnectionArguments | |
*/ | |
protected $arguments; | |
/** | |
* @var Edge[] | |
*/ | |
protected $edgeMap; | |
/** | |
* @var PageInfo | |
*/ | |
protected $pageInfo; | |
/** | |
* Connection constructor. | |
* | |
* @param array $data | |
* @param PageInfo $pageInfo | |
* @param ConnectionArguments $arguments | |
*/ | |
public function __construct(array $data, ConnectionArguments $arguments) | |
{ | |
$this->data = $data; | |
$this->arguments = $arguments; | |
$this->edgeMap = $this->createEdgeMap(); | |
$this->pageInfo = $this->createPageInfo(); | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function getEdges(): iterable | |
{ | |
$cursors = \array_keys($this->edgeMap); | |
$edges = \array_values($this->edgeMap); | |
$startOffset = \array_search($this->pageInfo->getStartCursor(), $cursors, true); | |
$endOffset = \array_search($this->pageInfo->getEndCursor(), $cursors, true); | |
$offset = false !== $startOffset | |
? $startOffset | |
: 0; | |
$length = false !== $endOffset | |
? $endOffset - $startOffset + 1 | |
: null; | |
return \array_slice($edges, $offset, $length, true); | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function getPageInfo(): PageInfo | |
{ | |
return $this->pageInfo; | |
} | |
/** | |
* @return Edge[] | |
*/ | |
protected function createEdgeMap(): array | |
{ | |
return \array_reduce($this->data, function ($data, NodeInterface $node) { | |
$cursor = $this->encodeCursor($node->createCursor($this->arguments)); | |
$edge = new Edge($cursor, $node); | |
$data[$cursor] = $edge; | |
return $data; | |
}, []); | |
} | |
/** | |
* @return PageInfo | |
*/ | |
protected function createPageInfo(): PageInfo | |
{ | |
return (new ArrayPageInfoBuilder($this->edgeMap, $this->arguments))->build(); | |
} | |
/** | |
* @param string $cursor | |
* @return string | |
*/ | |
protected function encodeCursor(string $cursor): string | |
{ | |
return base64_encode($cursor); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
use ALehdet\AspaApi\GraphQL\Relay\AbstractPageBuilder; | |
class ArrayPageInfoBuilder extends AbstractPageBuilder | |
{ | |
/** | |
* A list of cursors for edges in the connection. | |
* | |
* @var string[] | |
*/ | |
protected $cursors; | |
/** | |
* A list of edges in the connection. | |
* | |
* @var Edge[] | |
*/ | |
protected $edges; | |
/** | |
* The arguments given to the connection. | |
* | |
* @var ConnectionArguments | |
*/ | |
protected $arguments; | |
/** | |
* The number of selected items determined by the `first` or `last` argument. | |
* | |
* @var int|null | |
*/ | |
protected $selection; | |
/** | |
* The total number of edges in the connection. | |
* | |
* @var int | |
*/ | |
protected $total; | |
/** | |
* ArrayPageInfoBuilder constructor. | |
* | |
* @param array $edgeMap | |
* @param ConnectionArguments $arguments | |
* @param int|null $total | |
*/ | |
public function __construct(array $edgeMap, ConnectionArguments $arguments, ?int $total = null) | |
{ | |
$this->cursors = \array_keys($edgeMap); | |
$this->edges = \array_values($edgeMap); | |
$this->arguments = $arguments; | |
$this->selection = $arguments->getFirst() ?? $arguments->getLast(); | |
$this->total = $total ?? \count($edgeMap); | |
} | |
/** | |
* @return bool | |
* @throws PaginationException | |
*/ | |
public function hasPreviousPage(): bool | |
{ | |
return $this->getFirstIndex() > 0; | |
} | |
/** | |
* @return bool | |
* @throws PaginationException | |
*/ | |
public function hasNextPage(): bool | |
{ | |
return $this->getLastIndex() < ($this->total - 1); | |
} | |
/** | |
* @return Edge|null | |
* @throws PaginationException | |
*/ | |
public function getFirstEdge(): ?Edge | |
{ | |
return $this->edges[$this->getFirstIndex()] ?? null; | |
} | |
/** | |
* @return Edge|null | |
* @throws PaginationException | |
*/ | |
public function getLastEdge(): ?Edge | |
{ | |
return $this->edges[$this->getLastIndex()] ?? null; | |
} | |
/** | |
* @return int | |
* @throws PaginationException | |
*/ | |
protected function getFirstIndex(): int | |
{ | |
$selectionCount = $this->selection ?? $this->total; | |
$afterIndex = $this->getAfterIndex(); | |
if ($afterIndex >= 0) { | |
$index = $afterIndex + 1; | |
$this->assertUpperBounds($index); | |
return $index; | |
} | |
$beforeIndex = $this->getBeforeIndex(); | |
if ($beforeIndex >= 0) { | |
$index = $beforeIndex - $selectionCount; | |
$this->assertLowerBounds($index); | |
return $index; | |
} | |
return 0; | |
} | |
/** | |
* @return int | |
* @throws PaginationException | |
*/ | |
protected function getLastIndex(): int | |
{ | |
$selectionCount = $this->selection ?? $this->total; | |
$afterIndex = $this->getAfterIndex(); | |
if ($afterIndex >= 0) { | |
$index = $afterIndex + $selectionCount; | |
$this->assertUpperBounds($index); | |
return $index; | |
} | |
$beforeIndex = $this->getBeforeIndex(); | |
if ($beforeIndex >= 0) { | |
$index = $beforeIndex - 1; | |
$this->assertLowerBounds($index); | |
return $index; | |
} | |
$index = $selectionCount - 1; | |
return $index; | |
} | |
/** | |
* @return int | |
* @throws PaginationException | |
*/ | |
protected function getBeforeIndex(): int | |
{ | |
$before = $this->arguments->getBefore(); | |
if (null === $before) { | |
return -1; | |
} | |
if (false === ($index = \array_search($before, $this->cursors, true))) { | |
throw new PaginationException(\sprintf('Invalid cursor: "%s"', $before)); | |
} | |
return $index; | |
} | |
/** | |
* @return int | |
* @throws PaginationException | |
*/ | |
protected function getAfterIndex(): int | |
{ | |
$after = $this->arguments->getAfter(); | |
if (null === $after) { | |
return -1; | |
} | |
if (false === ($index = \array_search($after, $this->cursors, true))) { | |
throw new PaginationException(\sprintf('Invalid cursor: "%s"', $after)); | |
} | |
return $index; | |
} | |
/** | |
* @param int $index | |
* @throws PaginationException | |
*/ | |
protected function assertLowerBounds(int $index): void | |
{ | |
if ($index < 0) { | |
throw new PaginationException(\sprintf('Index out of bounds: %d', $index)); | |
} | |
} | |
/** | |
* @param int $index | |
* @throws PaginationException | |
*/ | |
protected function assertUpperBounds(int $index): void | |
{ | |
if ($index > $this->total) { | |
throw new PaginationException(\sprintf('Index out of bounds: %d', $index)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Test\Relay; | |
use Digia\GraphQL\Relay\ArrayPageInfoBuilder; | |
use Digia\GraphQL\Relay\ConnectionArguments; | |
use Digia\GraphQL\Relay\Edge; | |
use Digia\GraphQL\Relay\NodeInterface; | |
use Digia\GraphQL\Relay\PageInfo; | |
use Digia\GraphQL\Relay\PaginationException; | |
use PHPUnit\Framework\TestCase; | |
class ArrayPageInfoBuilderTest extends TestCase | |
{ | |
/** | |
* @var array | |
*/ | |
protected $productData; | |
public function setUp() | |
{ | |
$this->productData = [ | |
[ | |
'id' => '8hUrNOc', | |
'title' => 'Demi', | |
'description' => 'The Demi magazine.', | |
'createdAt' => '2018-04-18 18:54:32', | |
], | |
[ | |
'id' => '-QHEw3V', | |
'title' => 'Apu', | |
'description' => 'The Apu magazine.', | |
'createdAt' => '2018-04-18 18:54:37', | |
], | |
[ | |
'id' => 'BjOLIWM', | |
'title' => 'Kauneus ja Terveys', | |
'description' => 'The Kauneus ja Terveys magazine.', | |
'createdAt' => '2018-04-18 18:54:13', | |
], | |
[ | |
'id' => 'uEPthWb', | |
'title' => 'Meidän Talo', | |
'description' => 'The Meidän Talo magazine.', | |
'createdAt' => '2018-04-18 18:54:43', | |
], | |
[ | |
'id' => 'A9-6eZl', | |
'title' => 'Image', | |
'description' => 'The Image magazine.', | |
'createdAt' => '2018-04-18 18:54:48', | |
], | |
[ | |
'id' => 'v1e3oQq', | |
'title' => 'Eeva', | |
'description' => 'The Eeva magazine.', | |
'createdAt' => '2018-04-18 18:54:53', | |
] | |
]; | |
} | |
public function testWithoutArguments() | |
{ | |
$arguments = $this->createConnectionArguments(); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$pageInfo = $this->createPageInfo($edgeMap, $arguments); | |
$this->assertEquals('MjAxOC0wNC0xOCAxODo1NDozMg==', $pageInfo->getStartCursor()); | |
$this->assertEquals('MjAxOC0wNC0xOCAxODo1NDo1Mw==', $pageInfo->getEndCursor()); | |
$this->assertFalse($pageInfo->hasPreviousPage()); | |
$this->assertFalse($pageInfo->hasNextPage()); | |
} | |
public function testFirstAndAfter() | |
{ | |
$arguments = $this->createConnectionArguments([ | |
'first' => 3, | |
'after' => 'MjAxOC0wNC0xOCAxODo1NDoxMw==' | |
]); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$pageInfo = $this->createPageInfo($edgeMap, $arguments); | |
$this->assertEquals('MjAxOC0wNC0xOCAxODo1NDo0Mw==', $pageInfo->getStartCursor()); | |
$this->assertEquals('MjAxOC0wNC0xOCAxODo1NDo1Mw==', $pageInfo->getEndCursor()); | |
$this->assertTrue($pageInfo->hasPreviousPage()); | |
$this->assertFalse($pageInfo->hasNextPage()); | |
} | |
public function testInvalidFirst() | |
{ | |
$arguments = $this->createConnectionArguments([ | |
'first' => 100, | |
'after' => 'MjAxOC0wNC0xOCAxODo1NDoxMw==' | |
]); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$this->expectException(PaginationException::class); | |
$this->expectExceptionMessage('Index out of bounds: 102'); | |
$this->createPageInfo($edgeMap, $arguments); | |
} | |
public function testInvalidAfter() | |
{ | |
$arguments = $this->createConnectionArguments([ | |
'first' => 3, | |
'after' => 'SomeInvalidCursor' | |
]); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$this->expectException(PaginationException::class); | |
$this->expectExceptionMessage('Invalid cursor: "SomeInvalidCursor"'); | |
$this->createPageInfo($edgeMap, $arguments); | |
} | |
public function testLastAndBefore() | |
{ | |
$arguments = $this->createConnectionArguments([ | |
'last' => 2, | |
'before' => 'MjAxOC0wNC0xOCAxODo1NDo1Mw==' | |
]); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$pageInfo = $this->createPageInfo($edgeMap, $arguments); | |
$this->assertEquals('MjAxOC0wNC0xOCAxODo1NDo0Mw==', $pageInfo->getStartCursor()); | |
$this->assertEquals('MjAxOC0wNC0xOCAxODo1NDo0OA==', $pageInfo->getEndCursor()); | |
$this->assertTrue($pageInfo->hasPreviousPage()); | |
$this->assertTrue($pageInfo->hasNextPage()); | |
} | |
public function testInvalidLast() | |
{ | |
$arguments = $this->createConnectionArguments([ | |
'last' => 50, | |
'before' => 'MjAxOC0wNC0xOCAxODo1NDo1Mw==' | |
]); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$this->expectException(PaginationException::class); | |
$this->expectExceptionMessage('Index out of bounds: -45'); | |
$this->createPageInfo($edgeMap, $arguments); | |
} | |
public function testInvalidBefore() | |
{ | |
$arguments = $this->createConnectionArguments([ | |
'last' => 2, | |
'before' => 'SomeInvalidCursor' | |
]); | |
$edgeMap = $this->createEdgeMap($this->productData, $arguments); | |
$this->expectException(PaginationException::class); | |
$this->expectExceptionMessage('Invalid cursor: "SomeInvalidCursor"'); | |
$this->createPageInfo($edgeMap, $arguments); | |
} | |
private function createConnectionArguments(array $arguments = []): ConnectionArguments | |
{ | |
return ConnectionArguments::fromArray($arguments); | |
} | |
private function createEdgeMap(array $data, ConnectionArguments $arguments): array | |
{ | |
return \array_reduce($data, function ($data, $item) use ($arguments) { | |
$product = new Product($item['id'], $item['title'], $item['description'], $item['createdAt']); | |
$cursor = base64_encode($product->createCursor($arguments)); | |
$edge = new Edge($cursor, $product); | |
$data[$cursor] = $edge; | |
return $data; | |
}, []); | |
} | |
private function createPageInfo($edgeMap, $arguments, $total = null): PageInfo | |
{ | |
return (new ArrayPageInfoBuilder($edgeMap, $arguments, $total))->build(); | |
} | |
} | |
class Product implements NodeInterface | |
{ | |
public $id; | |
public $title; | |
public $description; | |
public $createdAt; | |
/** | |
* Product constructor. | |
* | |
* @param $id | |
* @param $title | |
* @param $description | |
* @param $createdAt | |
*/ | |
public function __construct($id, $title, $description, $createdAt) | |
{ | |
$this->id = $id; | |
$this->title = $title; | |
$this->description = $description; | |
$this->createdAt = $createdAt; | |
} | |
/** | |
* @param ConnectionArguments $arguments | |
* @return string | |
*/ | |
public function createCursor(ConnectionArguments $arguments): string | |
{ | |
return $this->createdAt; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
class ConnectionArguments | |
{ | |
/** | |
* @var string|null | |
*/ | |
protected $after; | |
/** | |
* @var string|null | |
*/ | |
protected $before; | |
/** | |
* @var int|null | |
*/ | |
protected $first; | |
/** | |
* @var int|null | |
*/ | |
protected $last; | |
/** | |
* ConnectionArguments constructor. | |
* | |
* @param null|string $after | |
* @param null|string $before | |
* @param int|null $first | |
* @param int|null $last | |
*/ | |
public function __construct(?string $after = null, ?string $before = null, ?int $first = null, ?int $last = null) | |
{ | |
$this->after = $after; | |
$this->before = $before; | |
$this->first = $first; | |
$this->last = $last; | |
} | |
/** | |
* @return null|string | |
*/ | |
public function getAfter(): ?string | |
{ | |
return $this->after; | |
} | |
/** | |
* @return null|string | |
*/ | |
public function getBefore(): ?string | |
{ | |
return $this->before; | |
} | |
/** | |
* @return int|null | |
*/ | |
public function getFirst(): ?int | |
{ | |
return $this->first; | |
} | |
/** | |
* @return int|null | |
*/ | |
public function getLast(): ?int | |
{ | |
return $this->last; | |
} | |
/** | |
* @param array $arguments | |
* @return ConnectionArguments | |
*/ | |
public static function fromArray(array $arguments): ConnectionArguments | |
{ | |
return new ConnectionArguments( | |
$arguments['after'] ?? null, | |
$arguments['before'] ?? null, | |
$arguments['first'] ?? null, | |
$arguments['last'] ?? null | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
interface ConnectionInterface | |
{ | |
/** | |
* @return iterable | |
*/ | |
public function getEdges(): iterable; | |
/** | |
* @return PageInfo | |
*/ | |
public function getPageInfo(): PageInfo; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ALehdet\AspaApi\GraphQL; | |
interface DataFetcherInterface | |
{ | |
/** | |
* @param int $first | |
* @param string $after | |
* @return iterable | |
*/ | |
public function fetchAfter(int $first, string $after): iterable; | |
/** | |
* @param int $last | |
* @param string $before | |
* @return iterable | |
*/ | |
public function fetchBefore(int $last, string $before): iterable; | |
/** | |
* @param int $first | |
* @return iterable | |
*/ | |
public function fetchFirst(int $first): iterable; | |
/** | |
* @param int $last | |
* @return iterable | |
*/ | |
public function fetchLast(int $last): iterable; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ALehdet\AspaApi\GraphQL; | |
use Digia\GraphQL\Relay\ConnectionArguments; | |
use Digia\GraphQL\Relay\ConnectionInterface; | |
use Digia\GraphQL\Relay\Edge; | |
use Digia\GraphQL\Relay\NodeInterface; | |
use Digia\GraphQL\Relay\PageInfo; | |
use Digia\GraphQL\Relay\RelayException; | |
class DoctrineConnection implements ConnectionInterface | |
{ | |
/** | |
* @var DataFetcherInterface | |
*/ | |
protected $dataFetcher; | |
/** | |
* @var ConnectionArguments | |
*/ | |
protected $arguments; | |
/** | |
* @var Edge[] | |
*/ | |
protected $edges; | |
/** | |
* @var PageInfo | |
*/ | |
protected $pageInfo; | |
/** | |
* DoctrineConnection constructor. | |
* | |
* @param DataFetcherInterface $dataFetcher | |
* @param ConnectionArguments $arguments | |
* @throws RelayException | |
*/ | |
public function __construct(DataFetcherInterface $dataFetcher, ConnectionArguments $arguments) | |
{ | |
$this->dataFetcher = $dataFetcher; | |
$this->arguments = $arguments; | |
$this->edges = $this->createEdges(); | |
$this->pageInfo = $this->createPageInfo(); | |
} | |
/** | |
* @return iterable | |
* @throws RelayException | |
*/ | |
public function getEdges(): iterable | |
{ | |
return $this->edges; | |
} | |
/** | |
* @return PageInfo | |
*/ | |
public function getPageInfo(): PageInfo | |
{ | |
return $this->pageInfo; | |
} | |
/** | |
* @return Edge[] | |
* @throws RelayException | |
*/ | |
protected function createEdges(): array | |
{ | |
return \array_reduce($this->fetchData(), function ($data, NodeInterface $node) { | |
$cursor = $this->encodeCursor($node->createCursor($this->arguments)); | |
$data[] = new Edge($cursor, $node); | |
return $data; | |
}, []); | |
} | |
/** | |
* @return PageInfo | |
*/ | |
protected function createPageInfo(): PageInfo | |
{ | |
return (new DoctrinePageInfoBuilder($this->edges, $this->arguments))->build(); | |
} | |
/** | |
* @return array | |
* @throws RelayException | |
*/ | |
protected function fetchData(): array | |
{ | |
$first = $this->arguments->getFirst(); | |
if (null !== $first) { | |
$after = $this->arguments->getAfter(); | |
return null !== $after | |
? $this->dataFetcher->fetchAfter($first, $this->decodeCursor($after)) | |
: $this->dataFetcher->fetchFirst($first); | |
} | |
$last = $this->arguments->getLast(); | |
if (null !== $last) { | |
$before = $this->arguments->getBefore(); | |
return null !== $before | |
? $this->dataFetcher->fetchBefore($last, $this->decodeCursor($before)) | |
: $this->dataFetcher->fetchLast($last); | |
} | |
throw new RelayException('You must provide a `first` or `last` value to properly paginate connections.'); | |
} | |
/** | |
* @param string $cursor | |
* @return string | |
*/ | |
protected function encodeCursor(string $cursor): string | |
{ | |
return base64_encode($cursor); | |
} | |
/** | |
* @param string $cursor | |
* @return string | |
*/ | |
protected function decodeCursor(string $cursor): string | |
{ | |
return base64_decode($cursor); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ALehdet\AspaApi\GraphQL; | |
use ALehdet\AspaApi\GraphQL\Relay\AbstractPageBuilder; | |
use Digia\GraphQL\Relay\ConnectionArguments; | |
use Digia\GraphQL\Relay\Edge; | |
class DoctrinePageInfoBuilder extends AbstractPageBuilder | |
{ | |
/** | |
* @var Edge[] | |
*/ | |
protected $edges; | |
/** | |
* @var ConnectionArguments | |
*/ | |
protected $arguments; | |
/** | |
* DoctrinePageInfoBuilder constructor. | |
* | |
* @param Edge[] $edges | |
* @param ConnectionArguments $arguments | |
*/ | |
public function __construct(array $edges, ConnectionArguments $arguments) | |
{ | |
$this->edges = $edges; | |
$this->arguments = $arguments; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function getFirstEdge(): ?Edge | |
{ | |
return $this->edges[0] ?? null; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function getLastEdge(): ?Edge | |
{ | |
return $this->edges[\count($this->edges) - 1] ?? null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
class Edge | |
{ | |
/** | |
* @var string | |
*/ | |
protected $cursor; | |
/** | |
* @var NodeInterface | |
*/ | |
protected $node; | |
/** | |
* Edge constructor. | |
* | |
* @param string $cursor | |
* @param NodeInterface $node | |
*/ | |
public function __construct(string $cursor, NodeInterface $node) | |
{ | |
$this->cursor = $cursor; | |
$this->node = $node; | |
} | |
/** | |
* @return string | |
*/ | |
public function getCursor(): string | |
{ | |
return $this->cursor; | |
} | |
/** | |
* @return NodeInterface | |
*/ | |
public function getNode(): NodeInterface | |
{ | |
return $this->node; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
interface NodeInterface | |
{ | |
/** | |
* @param ConnectionArguments $arguments | |
* @return string | |
*/ | |
public function createCursor(ConnectionArguments $arguments): string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
interface PageInfoBuilderInterface | |
{ | |
/** | |
* @return Edge|null | |
*/ | |
public function getFirstEdge(): ?Edge; | |
/** | |
* @return Edge|null | |
*/ | |
public function getLastEdge(): ?Edge; | |
/** | |
* @return bool | |
*/ | |
public function hasPreviousPage(): bool; | |
/** | |
* @return bool | |
*/ | |
public function hasNextPage(): bool; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Digia\GraphQL\Relay; | |
class PageInfo | |
{ | |
/** | |
* @var string|null | |
*/ | |
protected $startCursor; | |
/** | |
* @var string|null | |
*/ | |
protected $endCursor; | |
/** | |
* @var bool | |
*/ | |
protected $hasPreviousPage; | |
/** | |
* @var bool | |
*/ | |
protected $hasNextPage; | |
/** | |
* PageInfo constructor. | |
* | |
* @param null|string $startCursor | |
* @param null|string $endCursor | |
* @param bool $hasPreviousPage | |
* @param bool $hasNextPage | |
*/ | |
public function __construct( | |
?string $startCursor = null, | |
?string $endCursor = null, | |
bool $hasPreviousPage = false, | |
bool $hasNextPage = false | |
) { | |
$this->startCursor = $startCursor; | |
$this->endCursor = $endCursor; | |
$this->hasPreviousPage = $hasPreviousPage; | |
$this->hasNextPage = $hasNextPage; | |
} | |
/** | |
* @return null|string | |
*/ | |
public function getStartCursor(): ?string | |
{ | |
return $this->startCursor; | |
} | |
/** | |
* @return null|string | |
*/ | |
public function getEndCursor(): ?string | |
{ | |
return $this->endCursor; | |
} | |
/** | |
* @return bool | |
*/ | |
public function getHasPreviousPage(): bool | |
{ | |
return $this->hasPreviousPage; | |
} | |
/** | |
* @return bool | |
*/ | |
public function getHasNextPage(): bool | |
{ | |
return $this->hasNextPage; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment