Skip to content

Commit fbdc798

Browse files
author
Xavier Leune
committed
fix(graphql): remove count query if paginationInfo is not requested
1 parent af8726a commit fbdc798

File tree

6 files changed

+306
-118
lines changed

6 files changed

+306
-118
lines changed

src/GraphQl/Resolver/Factory/CollectionResolverFactory.php

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public function __invoke(string $resourceClass = null, string $rootClass = null,
4949
$resolverContext = ['source' => $source, 'args' => $args, 'info' => $info, 'is_collection' => true, 'is_mutation' => false, 'is_subscription' => false];
5050

5151
$collection = ($this->readStage)($resourceClass, $rootClass, $operation, $resolverContext);
52+
5253
if (!is_iterable($collection)) {
5354
throw new \LogicException('Collection from read stage should be iterable.');
5455
}

src/GraphQl/Resolver/Stage/SerializeStage.php

+66-43
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ public function __invoke(object|array|null $itemOrCollection, string $resourceCl
7878
$data = $this->normalizer->normalize($itemOrCollection, ItemNormalizer::FORMAT, $normalizationContext);
7979
}
8080
}
81-
8281
if ($isCollection && is_iterable($itemOrCollection)) {
8382
if (!$this->pagination->isGraphQlEnabled($operation, $context)) {
8483
$data = [];
@@ -88,7 +87,7 @@ public function __invoke(object|array|null $itemOrCollection, string $resourceCl
8887
} else {
8988
$data = 'cursor' === $this->pagination->getGraphQlPaginationType($operation) ?
9089
$this->serializeCursorBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context) :
91-
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext);
90+
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context);
9291
}
9392
}
9493

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

119+
$selection = $context['info']->getFieldSelection(1);
120+
120121
$offset = 0;
121122
$totalItems = 1; // For partial pagination, always consider there is at least one item.
122-
$nbPageItems = $collection->count();
123-
if (isset($args['after'])) {
124-
$after = base64_decode($args['after'], true);
125-
if (false === $after || '' === $args['after']) {
126-
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
123+
$data = ['edges' => []];
124+
if (isset($selection['pageInfo']) || isset($selection['totalCount']) || isset($selection['edges']['cursor'])) {
125+
$nbPageItems = $collection->count();
126+
if (isset($args['after'])) {
127+
$after = base64_decode($args['after'], true);
128+
if (false === $after || '' === $args['after']) {
129+
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
130+
}
131+
$offset = 1 + (int) $after;
127132
}
128-
$offset = 1 + (int) $after;
129-
}
130-
131-
if ($collection instanceof PaginatorInterface) {
132-
$totalItems = $collection->getTotalItems();
133133

134-
if (isset($args['before'])) {
135-
$before = base64_decode($args['before'], true);
136-
if (false === $before || '' === $args['before']) {
137-
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
134+
if ($collection instanceof PaginatorInterface && (isset($selection['pageInfo']) || isset($selection['totalCount']))) {
135+
$totalItems = $collection->getTotalItems();
136+
if (isset($args['before'])) {
137+
$before = base64_decode($args['before'], true);
138+
if (false === $before || '' === $args['before']) {
139+
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
140+
}
141+
$offset = (int) $before - $nbPageItems;
142+
}
143+
if (isset($args['last']) && !isset($args['before'])) {
144+
$offset = $totalItems - $args['last'];
138145
}
139-
$offset = (int) $before - $nbPageItems;
140-
}
141-
if (isset($args['last']) && !isset($args['before'])) {
142-
$offset = $totalItems - $args['last'];
143146
}
144-
}
145147

146-
$offset = 0 > $offset ? 0 : $offset;
147-
148-
$data = $this->getDefaultCursorBasedPaginatedData();
149-
if ($totalItems > 0) {
150-
$data['pageInfo']['startCursor'] = base64_encode((string) $offset);
151-
$end = $offset + $nbPageItems - 1;
152-
$data['pageInfo']['endCursor'] = base64_encode((string) ($end >= 0 ? $end : 0));
153-
$data['pageInfo']['hasPreviousPage'] = $offset > 0;
154-
if ($collection instanceof PaginatorInterface) {
155-
$data['totalCount'] = $totalItems;
156-
$itemsPerPage = $collection->getItemsPerPage();
157-
$data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
148+
$offset = max(0, $offset);
149+
150+
$data = $this->getDefaultCursorBasedPaginatedData();
151+
if ((isset($selection['pageInfo']) || isset($selection['totalCount'])) && $totalItems > 0) {
152+
isset($selection['pageInfo']['startCursor']) && $data['pageInfo']['startCursor'] = base64_encode((string) $offset);
153+
$end = $offset + $nbPageItems - 1;
154+
isset($selection['pageInfo']['endCursor']) && $data['pageInfo']['endCursor'] = base64_encode((string) max($end, 0));
155+
isset($selection['pageInfo']['hasPreviousPage']) && $data['pageInfo']['hasPreviousPage'] = $offset > 0;
156+
if ($collection instanceof PaginatorInterface) {
157+
isset($selection['totalCount']) && $data['totalCount'] = $totalItems;
158+
159+
$itemsPerPage = $collection->getItemsPerPage();
160+
isset($selection['pageInfo']['hasNextPage']) && $data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
161+
}
158162
}
159163
}
160164

161165
$index = 0;
162166
foreach ($collection as $object) {
163-
$data['edges'][$index] = [
167+
$edge = [
164168
'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext),
165-
'cursor' => base64_encode((string) ($index + $offset)),
166169
];
170+
if (isset($selection['edges']['cursor'])) {
171+
$edge['cursor'] = base64_encode((string) ($index + $offset));
172+
}
173+
$data['edges'][$index] = $edge;
167174
++$index;
168175
}
169176

@@ -173,17 +180,33 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
173180
/**
174181
* @throws \LogicException
175182
*/
176-
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext): array
183+
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext, array $context): array
177184
{
178-
if (!($collection instanceof PaginatorInterface)) {
179-
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s.', PaginatorInterface::class));
185+
$data = ['collection' => []];
186+
187+
$selection = $context['info']->getFieldSelection(1);
188+
if (isset($selection['paginationInfo'])) {
189+
$data['paginationInfo'] = [];
190+
if (isset($selection['paginationInfo']['itemsPerPage'])) {
191+
if (!($collection instanceof PartialPaginatorInterface)) {
192+
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return itemsPerPage field.', PartialPaginatorInterface::class));
193+
}
194+
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();
195+
}
196+
if (isset($selection['paginationInfo']['totalCount'])) {
197+
if (!($collection instanceof PaginatorInterface)) {
198+
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return totalCount field.', PaginatorInterface::class));
199+
}
200+
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
201+
}
202+
if (isset($selection['paginationInfo']['lastPage'])) {
203+
if (!($collection instanceof PaginatorInterface)) {
204+
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return lastPage field.', PaginatorInterface::class));
205+
}
206+
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
207+
}
180208
}
181209

182-
$data = $this->getDefaultPageBasedPaginatedData();
183-
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
184-
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
185-
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();
186-
187210
foreach ($collection as $object) {
188211
$data['collection'][] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext);
189212
}

src/GraphQl/State/Processor/NormalizeProcessor.php

+66-42
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private function getData(mixed $itemOrCollection, GraphQlOperation $operation, a
9898
} else {
9999
$data = 'cursor' === $this->pagination->getGraphQlPaginationType($operation) ?
100100
$this->serializeCursorBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context) :
101-
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext);
101+
$this->serializePageBasedPaginatedCollection($itemOrCollection, $normalizationContext, $context);
102102
}
103103
}
104104

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

132+
$selection = $context['info']->getFieldSelection(1);
133+
132134
$offset = 0;
133135
$totalItems = 1; // For partial pagination, always consider there is at least one item.
134-
$nbPageItems = $collection->count();
135-
if (isset($args['after'])) {
136-
$after = base64_decode($args['after'], true);
137-
if (false === $after || '' === $args['after']) {
138-
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
136+
$data = ['edges' => []];
137+
if (isset($selection['pageInfo']) || isset($selection['totalCount']) || isset($selection['edges']['cursor'])) {
138+
$nbPageItems = $collection->count();
139+
if (isset($args['after'])) {
140+
$after = base64_decode($args['after'], true);
141+
if (false === $after || '' === $args['after']) {
142+
throw new \UnexpectedValueException('' === $args['after'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['after']));
143+
}
144+
$offset = 1 + (int) $after;
139145
}
140-
$offset = 1 + (int) $after;
141-
}
142146

143-
if ($collection instanceof PaginatorInterface) {
144-
$totalItems = $collection->getTotalItems();
145-
146-
if (isset($args['before'])) {
147-
$before = base64_decode($args['before'], true);
148-
if (false === $before || '' === $args['before']) {
149-
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
147+
if ($collection instanceof PaginatorInterface && (isset($selection['pageInfo']) || isset($selection['totalCount']))) {
148+
$totalItems = $collection->getTotalItems();
149+
if (isset($args['before'])) {
150+
$before = base64_decode($args['before'], true);
151+
if (false === $before || '' === $args['before']) {
152+
throw new \UnexpectedValueException('' === $args['before'] ? 'Empty cursor is invalid' : sprintf('Cursor %s is invalid', $args['before']));
153+
}
154+
$offset = (int) $before - $nbPageItems;
155+
}
156+
if (isset($args['last']) && !isset($args['before'])) {
157+
$offset = $totalItems - $args['last'];
150158
}
151-
$offset = (int) $before - $nbPageItems;
152-
}
153-
if (isset($args['last']) && !isset($args['before'])) {
154-
$offset = $totalItems - $args['last'];
155159
}
156-
}
157160

158-
$offset = 0 > $offset ? 0 : $offset;
159-
160-
$data = $this->getDefaultCursorBasedPaginatedData();
161-
if ($totalItems > 0) {
162-
$data['pageInfo']['startCursor'] = base64_encode((string) $offset);
163-
$end = $offset + $nbPageItems - 1;
164-
$data['pageInfo']['endCursor'] = base64_encode((string) ($end >= 0 ? $end : 0));
165-
$data['pageInfo']['hasPreviousPage'] = $offset > 0;
166-
if ($collection instanceof PaginatorInterface) {
167-
$data['totalCount'] = $totalItems;
168-
$itemsPerPage = $collection->getItemsPerPage();
169-
$data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
161+
$offset = max(0, $offset);
162+
163+
$data = $this->getDefaultCursorBasedPaginatedData();
164+
if ((isset($selection['pageInfo']) || isset($selection['totalCount'])) && $totalItems > 0) {
165+
isset($selection['pageInfo']['startCursor']) && $data['pageInfo']['startCursor'] = base64_encode((string) $offset);
166+
$end = $offset + $nbPageItems - 1;
167+
isset($selection['pageInfo']['endCursor']) && $data['pageInfo']['endCursor'] = base64_encode((string) max($end, 0));
168+
isset($selection['pageInfo']['hasPreviousPage']) && $data['pageInfo']['hasPreviousPage'] = $offset > 0;
169+
if ($collection instanceof PaginatorInterface) {
170+
isset($selection['totalCount']) && $data['totalCount'] = $totalItems;
171+
172+
$itemsPerPage = $collection->getItemsPerPage();
173+
isset($selection['pageInfo']['hasNextPage']) && $data['pageInfo']['hasNextPage'] = (float) ($itemsPerPage > 0 ? $offset % $itemsPerPage : $offset) + $itemsPerPage * $collection->getCurrentPage() < $totalItems;
174+
}
170175
}
171176
}
172177

173178
$index = 0;
174179
foreach ($collection as $object) {
175-
$data['edges'][$index] = [
180+
$edge = [
176181
'node' => $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext),
177-
'cursor' => base64_encode((string) ($index + $offset)),
178182
];
183+
if (isset($selection['edges']['cursor'])) {
184+
$edge['cursor'] = base64_encode((string) ($index + $offset));
185+
}
186+
$data['edges'][$index] = $edge;
179187
++$index;
180188
}
181189

@@ -185,17 +193,33 @@ private function serializeCursorBasedPaginatedCollection(iterable $collection, a
185193
/**
186194
* @throws \LogicException
187195
*/
188-
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext): array
196+
private function serializePageBasedPaginatedCollection(iterable $collection, array $normalizationContext, array $context): array
189197
{
190-
if (!($collection instanceof PaginatorInterface)) {
191-
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s.', PaginatorInterface::class));
198+
$data = ['collection' => []];
199+
200+
$selection = $context['info']->getFieldSelection(1);
201+
if (isset($selection['paginationInfo'])) {
202+
$data['paginationInfo'] = [];
203+
if (isset($selection['paginationInfo']['itemsPerPage'])) {
204+
if (!($collection instanceof PartialPaginatorInterface)) {
205+
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return itemsPerPage field.', PartialPaginatorInterface::class));
206+
}
207+
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();
208+
}
209+
if (isset($selection['paginationInfo']['totalCount'])) {
210+
if (!($collection instanceof PaginatorInterface)) {
211+
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return totalCount field.', PaginatorInterface::class));
212+
}
213+
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
214+
}
215+
if (isset($selection['paginationInfo']['lastPage'])) {
216+
if (!($collection instanceof PaginatorInterface)) {
217+
throw new \LogicException(sprintf('Collection returned by the collection data provider must implement %s to return lastPage field.', PaginatorInterface::class));
218+
}
219+
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
220+
}
192221
}
193222

194-
$data = $this->getDefaultPageBasedPaginatedData();
195-
$data['paginationInfo']['totalCount'] = $collection->getTotalItems();
196-
$data['paginationInfo']['lastPage'] = $collection->getLastPage();
197-
$data['paginationInfo']['itemsPerPage'] = $collection->getItemsPerPage();
198-
199223
foreach ($collection as $object) {
200224
$data['collection'][] = $this->normalizer->normalize($object, ItemNormalizer::FORMAT, $normalizationContext);
201225
}

0 commit comments

Comments
 (0)