Skip to content

Commit

Permalink
fix(graphql): remove count query if paginationInfo is not requested (#…
Browse files Browse the repository at this point in the history
…6068)

Co-authored-by: Xavier Leune <[email protected]>
  • Loading branch information
xavierleune and Xavier Leune authored Jan 17, 2024
1 parent ee1e4ee commit ef4b261
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 117 deletions.
108 changes: 66 additions & 42 deletions src/GraphQl/Resolver/Stage/SerializeStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function __invoke(object|array|null $itemOrCollection, string $resourceCl
} else {
$data = 'cursor' === $this->pagination->getGraphQlPaginationType($operation) ?
$this->serializeCursorBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context) :
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext);
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context);
}
}

Expand Down Expand Up @@ -117,53 +117,61 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s or %s.', PaginatorInterface::class, PartialPaginatorInterface::class));
}

$selection = $context['info']->getFieldSelection(1);

$offset = 0;
$totalItems = 1; // For partial pagination, always consider there is at least one item.
$nbPageItems = $collection->count();
if (isset($args['after'])) {
$after = base64_decode($args['after'], true);
if (false === $after || '' === $args['after']) {
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
$data = ['edges' => []];
if (isset($selection['pageInfo']) || isset($selection['totalCount']) || isset($selection['edges']['cursor'])) {
$nbPageItems = $collection->count();
if (isset($args['after'])) {
$after = base64_decode($args['after'], true);
if (false === $after || '' === $args['after']) {
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
}
$offset = 1 + (int) $after;
}
$offset = 1 + (int) $after;
}

if ($collection instanceof PaginatorInterface) {
$totalItems = $collection->getTotalItems();

if (isset($args['before'])) {
$before = base64_decode($args['before'], true);
if (false === $before || '' === $args['before']) {
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
if ($collection instanceof PaginatorInterface && (isset($selection['pageInfo']) || isset($selection['totalCount']))) {
$totalItems = $collection->getTotalItems();
if (isset($args['before'])) {
$before = base64_decode($args['before'], true);
if (false === $before || '' === $args['before']) {
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
}
$offset = (int) $before - $nbPageItems;
}
if (isset($args['last']) && !isset($args['before'])) {
$offset = $totalItems - $args['last'];
}
$offset = (int) $before - $nbPageItems;
}
if (isset($args['last']) && !isset($args['before'])) {
$offset = $totalItems - $args['last'];
}
}

$offset = 0 > $offset ? 0 : $offset;

$data = $this->getDefaultCursorBasedPaginatedData();
if ($totalItems > 0) {
$data['pageInfo']['startCursor'] = base64_encode((string) $offset);
$end = $offset + $nbPageItems - 1;
$data['pageInfo']['endCursor'] = base64_encode((string) ($end >= 0 ? $end : 0));
$data['pageInfo']['hasPreviousPage'] = $offset > 0;
if ($collection instanceof PaginatorInterface) {
$data['totalCount'] = $totalItems;
$itemsPerPage = $collection->getItemsPerPage();
$data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
$offset = max(0, $offset);

$data = $this->getDefaultCursorBasedPaginatedData();
if ((isset($selection['pageInfo']) || isset($selection['totalCount'])) && $totalItems > 0) {
isset($selection['pageInfo']['startCursor']) && $data['pageInfo']['startCursor'] = base64_encode((string) $offset);
$end = $offset + $nbPageItems - 1;
isset($selection['pageInfo']['endCursor']) && $data['pageInfo']['endCursor'] = base64_encode((string) max($end, 0));
isset($selection['pageInfo']['hasPreviousPage']) && $data['pageInfo']['hasPreviousPage'] = $offset > 0;
if ($collection instanceof PaginatorInterface) {
isset($selection['totalCount']) && $data['totalCount'] = $totalItems;

$itemsPerPage = $collection->getItemsPerPage();
isset($selection['pageInfo']['hasNextPage']) && $data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
}
}
}

$index = 0;
foreach ($collection as $object) {
$data['edges'][$index] = [
$edge = [
'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext),
'cursor' => base64_encode((string) ($index + $offset)),
];
if (isset($selection['edges']['cursor'])) {
$edge['cursor'] = base64_encode((string) ($index + $offset));
}
$data['edges'][$index] = $edge;
++$index;
}

Expand All @@ -173,17 +181,33 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
/**
* @throws \LogicException
*/
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext): array
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext, array $context): array
{
if (!($collection instanceof PaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s.', PaginatorInterface::class));
$data = ['collection' => []];

$selection = $context['info']->getFieldSelection(1);
if (isset($selection['paginationInfo'])) {
$data['paginationInfo'] = [];
if (isset($selection['paginationInfo']['itemsPerPage'])) {
if (!($collection instanceof PartialPaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return itemsPerPage field.', PartialPaginatorInterface::class));
}
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();
}
if (isset($selection['paginationInfo']['totalCount'])) {
if (!($collection instanceof PaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return totalCount field.', PaginatorInterface::class));
}
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
}
if (isset($selection['paginationInfo']['lastPage'])) {
if (!($collection instanceof PaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return lastPage field.', PaginatorInterface::class));
}
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
}
}

$data = $this->getDefaultPageBasedPaginatedData();
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();

foreach ($collection as $object) {
$data['collection'][] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext);
}
Expand Down
108 changes: 66 additions & 42 deletions src/GraphQl/State/Processor/NormalizeProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private function getData(mixed $itemOrCollection, GraphQlOperation $operation, a
} else {
$data = 'cursor' === $this->pagination->getGraphQlPaginationType($operation) ?
$this->serializeCursorBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context) :
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext);
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context);
}
}

Expand Down Expand Up @@ -129,53 +129,61 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s or %s.', PaginatorInterface::class, PartialPaginatorInterface::class));
}

$selection = $context['info']->getFieldSelection(1);

$offset = 0;
$totalItems = 1; // For partial pagination, always consider there is at least one item.
$nbPageItems = $collection->count();
if (isset($args['after'])) {
$after = base64_decode($args['after'], true);
if (false === $after || '' === $args['after']) {
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
$data = ['edges' => []];
if (isset($selection['pageInfo']) || isset($selection['totalCount']) || isset($selection['edges']['cursor'])) {
$nbPageItems = $collection->count();
if (isset($args['after'])) {
$after = base64_decode($args['after'], true);
if (false === $after || '' === $args['after']) {
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
}
$offset = 1 + (int) $after;
}
$offset = 1 + (int) $after;
}

if ($collection instanceof PaginatorInterface) {
$totalItems = $collection->getTotalItems();

if (isset($args['before'])) {
$before = base64_decode($args['before'], true);
if (false === $before || '' === $args['before']) {
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
if ($collection instanceof PaginatorInterface && (isset($selection['pageInfo']) || isset($selection['totalCount']))) {
$totalItems = $collection->getTotalItems();
if (isset($args['before'])) {
$before = base64_decode($args['before'], true);
if (false === $before || '' === $args['before']) {
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
}
$offset = (int) $before - $nbPageItems;
}
if (isset($args['last']) && !isset($args['before'])) {
$offset = $totalItems - $args['last'];
}
$offset = (int) $before - $nbPageItems;
}
if (isset($args['last']) && !isset($args['before'])) {
$offset = $totalItems - $args['last'];
}
}

$offset = 0 > $offset ? 0 : $offset;

$data = $this->getDefaultCursorBasedPaginatedData();
if ($totalItems > 0) {
$data['pageInfo']['startCursor'] = base64_encode((string) $offset);
$end = $offset + $nbPageItems - 1;
$data['pageInfo']['endCursor'] = base64_encode((string) ($end >= 0 ? $end : 0));
$data['pageInfo']['hasPreviousPage'] = $offset > 0;
if ($collection instanceof PaginatorInterface) {
$data['totalCount'] = $totalItems;
$itemsPerPage = $collection->getItemsPerPage();
$data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
$offset = max(0, $offset);

$data = $this->getDefaultCursorBasedPaginatedData();
if ((isset($selection['pageInfo']) || isset($selection['totalCount'])) && $totalItems > 0) {
isset($selection['pageInfo']['startCursor']) && $data['pageInfo']['startCursor'] = base64_encode((string) $offset);
$end = $offset + $nbPageItems - 1;
isset($selection['pageInfo']['endCursor']) && $data['pageInfo']['endCursor'] = base64_encode((string) max($end, 0));
isset($selection['pageInfo']['hasPreviousPage']) && $data['pageInfo']['hasPreviousPage'] = $offset > 0;
if ($collection instanceof PaginatorInterface) {
isset($selection['totalCount']) && $data['totalCount'] = $totalItems;

$itemsPerPage = $collection->getItemsPerPage();
isset($selection['pageInfo']['hasNextPage']) && $data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
}
}
}

$index = 0;
foreach ($collection as $object) {
$data['edges'][$index] = [
$edge = [
'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext),
'cursor' => base64_encode((string) ($index + $offset)),
];
if (isset($selection['edges']['cursor'])) {
$edge['cursor'] = base64_encode((string) ($index + $offset));
}
$data['edges'][$index] = $edge;
++$index;
}

Expand All @@ -185,17 +193,33 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
/**
* @throws \LogicException
*/
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext): array
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext, array $context): array
{
if (!($collection instanceof PaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s.', PaginatorInterface::class));
$data = ['collection' => []];

$selection = $context['info']->getFieldSelection(1);
if (isset($selection['paginationInfo'])) {
$data['paginationInfo'] = [];
if (isset($selection['paginationInfo']['itemsPerPage'])) {
if (!($collection instanceof PartialPaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return itemsPerPage field.', PartialPaginatorInterface::class));
}
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();
}
if (isset($selection['paginationInfo']['totalCount'])) {
if (!($collection instanceof PaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return totalCount field.', PaginatorInterface::class));
}
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
}
if (isset($selection['paginationInfo']['lastPage'])) {
if (!($collection instanceof PaginatorInterface)) {
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return lastPage field.', PaginatorInterface::class));
}
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
}
}

$data = $this->getDefaultPageBasedPaginatedData();
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();

foreach ($collection as $object) {
$data['collection'][] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext);
}
Expand Down
Loading

0 comments on commit ef4b261

Please sign in to comment.