From c9dd41bb7b17c08af49ef02bf60709d64bc24a6c Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 5 Dec 2023 11:35:12 +0100 Subject: [PATCH 1/6] Return empty results for pagination `first: 0` Resolves https://github.com/nuwave/lighthouse/issues/2472 --- CHANGELOG.md | 4 ++ .../ModelsLoader/PaginatedModelsLoader.php | 4 +- src/Pagination/PaginateDirective.php | 54 ++++------------ src/Pagination/PaginationArgs.php | 49 ++++++++++++++- ....php => ZeroFirstLengthAwarePaginator.php} | 2 +- src/Pagination/ZeroFirstPaginator.php | 19 ++++++ src/Schema/Directives/RelationDirective.php | 6 +- .../Pagination/PaginateDirectiveDBTest.php | 62 +++++++++++++++++-- .../Directives/HasManyDirectiveTest.php | 57 +++++++++++++++++ .../Unit/Pagination/PaginateDirectiveTest.php | 52 +++++++++++++--- 10 files changed, 247 insertions(+), 62 deletions(-) rename src/Pagination/{ZeroPageLengthAwarePaginator.php => ZeroFirstLengthAwarePaginator.php} (87%) create mode 100644 src/Pagination/ZeroFirstPaginator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 048c69ce6f..3bed311cdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co ## Unreleased +### Fixed + +- Return empty results for pagination `first: 0` + ## v6.24.0 ### Added diff --git a/src/Execution/ModelsLoader/PaginatedModelsLoader.php b/src/Execution/ModelsLoader/PaginatedModelsLoader.php index 5a0861f2ad..bd57b3ee9b 100644 --- a/src/Execution/ModelsLoader/PaginatedModelsLoader.php +++ b/src/Execution/ModelsLoader/PaginatedModelsLoader.php @@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Pagination\LengthAwarePaginator; use Nuwave\Lighthouse\Pagination\PaginationArgs; -use Nuwave\Lighthouse\Pagination\ZeroPageLengthAwarePaginator; +use Nuwave\Lighthouse\Pagination\ZeroFirstLengthAwarePaginator; use Nuwave\Lighthouse\Support\Utils; class PaginatedModelsLoader implements ModelsLoader @@ -185,7 +185,7 @@ protected function convertRelationToPaginator(EloquentCollection $parents): void $total = CountModelsLoader::extractCount($model, $this->relation); $paginator = $first === 0 - ? new ZeroPageLengthAwarePaginator($total, $page) + ? new ZeroFirstLengthAwarePaginator($total, $page) : new LengthAwarePaginator($model->getRelation($this->relation), $total, $first, $page); $model->setRelation($this->relation, $paginator); diff --git a/src/Pagination/PaginateDirective.php b/src/Pagination/PaginateDirective.php index d1c2e904b4..e15080cfa0 100644 --- a/src/Pagination/PaginateDirective.php +++ b/src/Pagination/PaginateDirective.php @@ -5,18 +5,17 @@ use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; use GraphQL\Language\AST\ObjectTypeDefinitionNode; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Contracts\Pagination\Paginator; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Query\Builder as QueryBuilder; use Laravel\Scout\Builder as ScoutBuilder; -use Nuwave\Lighthouse\Cache\CacheDirective; use Nuwave\Lighthouse\Execution\ResolveInfo; use Nuwave\Lighthouse\Schema\AST\DocumentAST; use Nuwave\Lighthouse\Schema\Directives\BaseDirective; use Nuwave\Lighthouse\Schema\Values\FieldValue; use Nuwave\Lighthouse\Support\Contracts\ComplexityResolverDirective; -use Nuwave\Lighthouse\Support\Contracts\Directive; use Nuwave\Lighthouse\Support\Contracts\FieldManipulator; use Nuwave\Lighthouse\Support\Contracts\FieldResolver; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; @@ -138,25 +137,28 @@ public function manipulateFieldDefinition(DocumentAST &$documentAST, FieldDefini public function resolveField(FieldValue $fieldValue): callable { return function (mixed $root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): Paginator { - if ($this->directiveHasArgument('resolver')) { - // This is done only for validation - PaginationArgs::extractArgs($args, $this->paginationType(), $this->paginateMaxCount()); + $paginationArgs = PaginationArgs::extractArgs($args, $resolveInfo, $this->paginationType(), $this->paginateMaxCount()); + if ($this->directiveHasArgument('resolver')) { $paginator = $this->getResolverFromArgument('resolver')($root, $args, $context, $resolveInfo); - assert( $paginator instanceof Paginator, "The method referenced by the resolver argument of the @{$this->name()} directive on {$this->nodeName()} must return a Paginator.", ); + if ($paginationArgs->first === 0) { + if ($paginator instanceof LengthAwarePaginator) { + return new ZeroFirstLengthAwarePaginator($paginator->total(), $paginationArgs->page); + } + + return new ZeroFirstPaginator($paginationArgs->page); + } + return $paginator; } if ($this->directiveHasArgument('builder')) { - $builderResolver = $this->getResolverFromArgument('builder'); - - $query = $builderResolver($root, $args, $context, $resolveInfo); - + $query = $this->getResolverFromArgument('builder')($root, $args, $context, $resolveInfo); assert( $query instanceof QueryBuilder || $query instanceof EloquentBuilder || $query instanceof ScoutBuilder || $query instanceof Relation, "The method referenced by the builder argument of the @{$this->name()} directive on {$this->nodeName()} must return a Builder or Relation.", @@ -174,42 +176,10 @@ public function resolveField(FieldValue $fieldValue): callable $resolveInfo, ); - $paginationArgs = PaginationArgs::extractArgs($args, $this->paginationType(), $this->paginateMaxCount()); - - $paginationArgs->type = $this->optimalPaginationType($resolveInfo); - return $paginationArgs->applyToBuilder($query); }; } - protected function optimalPaginationType(ResolveInfo $resolveInfo): PaginationType - { - $type = $this->paginationType(); - - // Already the most optimal type. - if ($type->isSimple()) { - return $type; - } - - // If the result may be used in a cache, we always want to retrieve and store the full pagination data. - // Even though the query that initially creates the cache may not need additional information such as - // the total counts, following queries may need them - and use the same cached value. - $hasCacheDirective = $resolveInfo->argumentSet - ->directives - ->contains(static fn (Directive $directive): bool => $directive instanceof CacheDirective); - if ($hasCacheDirective) { - return $type; - } - - // If the page info is not requested, we can save a database query by using the simple paginator. - // In contrast to the full pagination, it does not query total counts. - if (! isset($resolveInfo->getFieldSelection()[$type->infoFieldName()])) { - return new PaginationType(PaginationType::SIMPLE); - } - - return $type; - } - protected function paginationType(): PaginationType { return new PaginationType( diff --git a/src/Pagination/PaginationArgs.php b/src/Pagination/PaginationArgs.php index b2a52e5e3e..7ddad59655 100644 --- a/src/Pagination/PaginationArgs.php +++ b/src/Pagination/PaginationArgs.php @@ -9,6 +9,9 @@ use Illuminate\Database\Query\Builder as QueryBuilder; use Illuminate\Support\Arr; use Laravel\Scout\Builder as ScoutBuilder; +use Nuwave\Lighthouse\Cache\CacheDirective; +use Nuwave\Lighthouse\Execution\ResolveInfo; +use Nuwave\Lighthouse\Support\Contracts\Directive; class PaginationArgs { @@ -23,10 +26,11 @@ public function __construct( * * @param array $args */ - public static function extractArgs(array $args, PaginationType $paginationType, ?int $paginateMaxCount): self + public static function extractArgs(array $args, ResolveInfo $resolveInfo, PaginationType $proposedPaginationType, ?int $paginateMaxCount): self { $first = $args['first']; - $page = $paginationType->isConnection() + + $page = $proposedPaginationType->isConnection() ? self::calculateCurrentPage( $first, Cursor::decode($args), @@ -50,7 +54,9 @@ public static function extractArgs(array $args, PaginationType $paginationType, ); } - return new static($page, $first, $paginationType); + $optimalPaginationType = self::optimalPaginationType($proposedPaginationType, $resolveInfo); + + return new static($page, $first, $optimalPaginationType); } public static function requestedLessThanZeroItems(int $amount): string @@ -71,6 +77,32 @@ protected static function calculateCurrentPage(int $first, int $after, int $defa : $defaultPage; } + protected static function optimalPaginationType(PaginationType $proposedType, ResolveInfo $resolveInfo): PaginationType + { + // Already the most optimal type. + if ($proposedType->isSimple()) { + return $proposedType; + } + + // If the result may be used in a cache, we always want to retrieve and store the full pagination data. + // Even though the query that initially creates the cache may not need additional information such as + // the total counts, following queries may need them - and use the same cached value. + $hasCacheDirective = $resolveInfo->argumentSet + ->directives + ->contains(static fn (Directive $directive): bool => $directive instanceof CacheDirective); + if ($hasCacheDirective) { + return $proposedType; + } + + // If the page info is not requested, we can save a database query by using the simple paginator. + // In contrast to the full pagination, it does not query total counts. + if (! isset($resolveInfo->getFieldSelection()[$proposedType->infoFieldName()])) { + return new PaginationType(PaginationType::SIMPLE); + } + + return $proposedType; + } + /** * Apply the args to a builder, constructing a paginator. * @@ -82,6 +114,17 @@ protected static function calculateCurrentPage(int $first, int $after, int $defa */ public function applyToBuilder(QueryBuilder|ScoutBuilder|EloquentBuilder|Relation $builder): Paginator { + if ($this->first === 0) { + if ($this->type->isSimple()) { + return new ZeroFirstPaginator($this->page); + } + + return new ZeroFirstLengthAwarePaginator( + $builder->count(), + $this->page, + ); + } + $methodName = $this->type->isSimple() ? 'simplePaginate' : 'paginate'; diff --git a/src/Pagination/ZeroPageLengthAwarePaginator.php b/src/Pagination/ZeroFirstLengthAwarePaginator.php similarity index 87% rename from src/Pagination/ZeroPageLengthAwarePaginator.php rename to src/Pagination/ZeroFirstLengthAwarePaginator.php index a6a99b5dc9..d3a216c54d 100644 --- a/src/Pagination/ZeroPageLengthAwarePaginator.php +++ b/src/Pagination/ZeroFirstLengthAwarePaginator.php @@ -8,7 +8,7 @@ /** * @extends \Illuminate\Pagination\LengthAwarePaginator */ -class ZeroPageLengthAwarePaginator extends LengthAwarePaginator +class ZeroFirstLengthAwarePaginator extends LengthAwarePaginator { public function __construct(int $total, int $page) { diff --git a/src/Pagination/ZeroFirstPaginator.php b/src/Pagination/ZeroFirstPaginator.php new file mode 100644 index 0000000000..48f4b59ebb --- /dev/null +++ b/src/Pagination/ZeroFirstPaginator.php @@ -0,0 +1,19 @@ + + */ +class ZeroFirstPaginator extends Paginator +{ + public function __construct(int $page) + { + $this->perPage = 0; + $this->currentPage = $page; + $this->items = new Collection(); + } +} diff --git a/src/Schema/Directives/RelationDirective.php b/src/Schema/Directives/RelationDirective.php index e29b587ce3..0368ca6de4 100644 --- a/src/Schema/Directives/RelationDirective.php +++ b/src/Schema/Directives/RelationDirective.php @@ -45,7 +45,7 @@ public function resolveField(FieldValue $fieldValue): callable return function (Model $parent, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($relationName) { $decorateBuilder = $this->makeBuilderDecorator($parent, $args, $context, $resolveInfo); - $paginationArgs = $this->paginationArgs($args); + $paginationArgs = $this->paginationArgs($args, $resolveInfo); $relation = $parent->{$relationName}(); assert($relation instanceof Relation); @@ -140,12 +140,12 @@ protected function edgeType(DocumentAST $documentAST): ?ObjectTypeDefinitionNode } /** @param array $args */ - protected function paginationArgs(array $args): ?PaginationArgs + protected function paginationArgs(array $args, ResolveInfo $resolveInfo): ?PaginationArgs { $paginationType = $this->paginationType(); return $paginationType !== null - ? PaginationArgs::extractArgs($args, $paginationType, $this->paginationMaxCount()) + ? PaginationArgs::extractArgs($args, $resolveInfo, $paginationType, $this->paginationMaxCount()) : null; } diff --git a/tests/Integration/Pagination/PaginateDirectiveDBTest.php b/tests/Integration/Pagination/PaginateDirectiveDBTest.php index 557359c62c..95825db72c 100644 --- a/tests/Integration/Pagination/PaginateDirectiveDBTest.php +++ b/tests/Integration/Pagination/PaginateDirectiveDBTest.php @@ -450,7 +450,7 @@ public function testQueriesConnectionWithNoData(): void } } } - ')->assertJson([ + ')->assertExactJson([ 'data' => [ 'users' => [ 'pageInfo' => [ @@ -463,9 +463,10 @@ public function testQueriesConnectionWithNoData(): void 'startCursor' => null, 'total' => 0, ], + 'edges' => [], ], ], - ])->assertJsonCount(0, 'data.users.edges'); + ]); } public function testQueriesPaginationWithNoData(): void @@ -498,7 +499,7 @@ public function testQueriesPaginationWithNoData(): void } } } - ')->assertJson([ + ')->assertExactJson([ 'data' => [ 'users' => [ 'paginatorInfo' => [ @@ -511,9 +512,62 @@ public function testQueriesPaginationWithNoData(): void 'perPage' => 5, 'total' => 0, ], + 'data' => [], + ], + ], + ]); + } + + public function testQueriesFirst0(): void + { + $amount = 3; + factory(User::class, $amount)->create(); + + $this->schema = /** @lang GraphQL */ ' + type User { + id: ID! + } + + type Query { + users: [User!]! @paginate + } + '; + + $this->graphQL(/** @lang GraphQL */ ' + { + users(first: 0) { + paginatorInfo { + count + currentPage + firstItem + hasMorePages + lastItem + lastPage + perPage + total + } + data { + id + } + } + } + ')->assertExactJson([ + 'data' => [ + 'users' => [ + 'paginatorInfo' => [ + 'count' => 0, + 'currentPage' => 1, + 'firstItem' => null, + 'hasMorePages' => false, + 'lastItem' => null, + 'lastPage' => 0, + 'perPage' => 0, + 'total' => $amount, + ], + 'data' => [], ], ], - ])->assertJsonCount(0, 'data.users.data'); + ]); } public function testQueriesPaginationWithoutPaginatorInfo(): void diff --git a/tests/Integration/Schema/Directives/HasManyDirectiveTest.php b/tests/Integration/Schema/Directives/HasManyDirectiveTest.php index 34ff90598f..039b7b8f4f 100644 --- a/tests/Integration/Schema/Directives/HasManyDirectiveTest.php +++ b/tests/Integration/Schema/Directives/HasManyDirectiveTest.php @@ -297,6 +297,63 @@ public function testQueryPaginatedHasManyWithConditionInDifferentAliases(): void ]); } + public function testQueryPaginatedHasManyFirst0(): void + { + $this->schema = /** @lang GraphQL */ ' + type User { + id: ID! + tasks: [Task!]! @hasMany(type: PAGINATOR) + } + + type Task { + id: ID! + } + + type Query { + users: [User!]! @all + } + '; + + $user = factory(User::class)->create(); + assert($user instanceof User); + + $tasksCount = 3; + $tasks = factory(Task::class, $tasksCount)->make(); + $user->tasks()->saveMany($tasks); + + $this + ->graphQL(/** @lang GraphQL */ ' + { + users { + id + tasks(first: 0) { + paginatorInfo { + total + } + data { + id + } + } + } + } + ') + ->assertExactJson([ + 'data' => [ + 'users' => [ + [ + 'id' => (string) $user->id, + 'tasks' => [ + 'paginatorInfo' => [ + 'total' => $tasksCount, + ], + 'data' => [], + ], + ], + ], + ], + ]); + } + public function testQueryPaginatedHasManyWithNonUniqueForeignKey(): void { $this->schema = /** @lang GraphQL */ ' diff --git a/tests/Unit/Pagination/PaginateDirectiveTest.php b/tests/Unit/Pagination/PaginateDirectiveTest.php index 12356f2147..6adb41892a 100644 --- a/tests/Unit/Pagination/PaginateDirectiveTest.php +++ b/tests/Unit/Pagination/PaginateDirectiveTest.php @@ -14,6 +14,7 @@ use Nuwave\Lighthouse\Pagination\PaginationArgs; use Nuwave\Lighthouse\Pagination\PaginationType; use Tests\TestCase; +use Tests\Utils\Models\User; final class PaginateDirectiveTest extends TestCase { @@ -444,14 +445,13 @@ public function testIsLimitedByMaxCountFromDirective(): void } type Query { - users1: [User!]! @paginate(maxCount: 6) - users2: [User!]! @paginate(maxCount: 10) + users: [User!]! @paginate(maxCount: 6) } '; $result = $this->graphQL(/** @lang GraphQL */ ' { - users1(first: 10) { + users(first: 10) { data { id name @@ -692,8 +692,40 @@ public function testDisallowFirstNull(): void ')->assertGraphQLErrorMessage('Expected value of type "Int!", found null.'); } - /** @return \Illuminate\Pagination\LengthAwarePaginator> */ - public static function returnPaginatedDataInsteadOfBuilder(): LengthAwarePaginator + public function testQueriesFirst0SimplePaginator(): void + { + $this->schema = /** @lang GraphQL */ ' + type User { + id: ID! + } + + type Query { + users: [User!]! @paginate + } + '; + + $this->graphQL(/** @lang GraphQL */ ' + { + users(first: 0) { + data { + id + } + } + } + ')->assertExactJson([ + 'data' => [ + 'users' => [ + 'data' => [], + ], + ], + ]); + } + + /** + * @param array{first: int} $args + * @return \Illuminate\Pagination\LengthAwarePaginator> + */ + public static function returnPaginatedDataInsteadOfBuilder(mixed $root, array $args): LengthAwarePaginator { return new LengthAwarePaginator([ [ @@ -702,7 +734,7 @@ public static function returnPaginatedDataInsteadOfBuilder(): LengthAwarePaginat [ 'id' => 2, ], - ], 2, 15); + ], 2, $args['first']); } public function testPaginatorResolver(): void @@ -719,7 +751,10 @@ public function testPaginatorResolver(): void $this->graphQL(/* @lang GraphQL */ ' { - users(first: 0) { + users(first: 5) { + paginatorInfo { + perPage + } data { id } @@ -728,6 +763,9 @@ public function testPaginatorResolver(): void ')->assertJson([ 'data' => [ 'users' => [ + 'paginatorInfo' => [ + 'perPage' => 5, + ], 'data' => [ ['id' => 1], ['id' => 2], From 68450b1556304fd541c590388dcf568208b84d8b Mon Sep 17 00:00:00 2001 From: spawnia Date: Tue, 5 Dec 2023 10:35:57 +0000 Subject: [PATCH 2/6] Apply php-cs-fixer changes --- tests/Unit/Pagination/PaginateDirectiveTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Pagination/PaginateDirectiveTest.php b/tests/Unit/Pagination/PaginateDirectiveTest.php index 6adb41892a..5a74a1f3b9 100644 --- a/tests/Unit/Pagination/PaginateDirectiveTest.php +++ b/tests/Unit/Pagination/PaginateDirectiveTest.php @@ -14,7 +14,6 @@ use Nuwave\Lighthouse\Pagination\PaginationArgs; use Nuwave\Lighthouse\Pagination\PaginationType; use Tests\TestCase; -use Tests\Utils\Models\User; final class PaginateDirectiveTest extends TestCase { @@ -723,6 +722,7 @@ public function testQueriesFirst0SimplePaginator(): void /** * @param array{first: int} $args + * * @return \Illuminate\Pagination\LengthAwarePaginator> */ public static function returnPaginatedDataInsteadOfBuilder(mixed $root, array $args): LengthAwarePaginator From fd162060023f2f17b422a468f145601cb073e288 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 5 Dec 2023 11:42:41 +0100 Subject: [PATCH 3/6] fix stan --- src/Pagination/PaginationArgs.php | 8 ++++---- src/Pagination/ZeroFirstPaginator.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Pagination/PaginationArgs.php b/src/Pagination/PaginationArgs.php index 7ddad59655..1ff9ac20d0 100644 --- a/src/Pagination/PaginationArgs.php +++ b/src/Pagination/PaginationArgs.php @@ -119,10 +119,10 @@ public function applyToBuilder(QueryBuilder|ScoutBuilder|EloquentBuilder|Relatio return new ZeroFirstPaginator($this->page); } - return new ZeroFirstLengthAwarePaginator( - $builder->count(), - $this->page, - ); + $total = $builder instanceof ScoutBuilder + ? 0 // Laravel\Scout\Builder exposes no method to get the total count + : $builder->count(); // @phpstan-ignore-line see Illuminate\Database\Query\Builder::count(), available as a mixin in the other classes + return new ZeroFirstLengthAwarePaginator($total, $this->page); } $methodName = $this->type->isSimple() diff --git a/src/Pagination/ZeroFirstPaginator.php b/src/Pagination/ZeroFirstPaginator.php index 48f4b59ebb..c5b270b702 100644 --- a/src/Pagination/ZeroFirstPaginator.php +++ b/src/Pagination/ZeroFirstPaginator.php @@ -6,7 +6,7 @@ use Illuminate\Support\Collection; /** - * @extends \Illuminate\Pagination\LengthAwarePaginator + * @extends \Illuminate\Pagination\Paginator */ class ZeroFirstPaginator extends Paginator { From fd684674ab2c66bd9c5094ca09b46d9d92bb3b2a Mon Sep 17 00:00:00 2001 From: spawnia Date: Tue, 5 Dec 2023 10:43:27 +0000 Subject: [PATCH 4/6] Apply php-cs-fixer changes --- src/Pagination/PaginationArgs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Pagination/PaginationArgs.php b/src/Pagination/PaginationArgs.php index 1ff9ac20d0..e998edf854 100644 --- a/src/Pagination/PaginationArgs.php +++ b/src/Pagination/PaginationArgs.php @@ -122,6 +122,7 @@ public function applyToBuilder(QueryBuilder|ScoutBuilder|EloquentBuilder|Relatio $total = $builder instanceof ScoutBuilder ? 0 // Laravel\Scout\Builder exposes no method to get the total count : $builder->count(); // @phpstan-ignore-line see Illuminate\Database\Query\Builder::count(), available as a mixin in the other classes + return new ZeroFirstLengthAwarePaginator($total, $this->page); } From 39d840819baa534ac9ec91010ebf108182077bd6 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 5 Dec 2023 11:43:49 +0100 Subject: [PATCH 5/6] perPage --- src/Execution/ModelsLoader/PaginatedModelsLoader.php | 4 ++-- src/Pagination/PaginateDirective.php | 4 ++-- src/Pagination/PaginationArgs.php | 4 ++-- ...arePaginator.php => ZeroPerPageLengthAwarePaginator.php} | 6 ++---- .../{ZeroFirstPaginator.php => ZeroPerPagePaginator.php} | 6 ++---- 5 files changed, 10 insertions(+), 14 deletions(-) rename src/Pagination/{ZeroFirstLengthAwarePaginator.php => ZeroPerPageLengthAwarePaginator.php} (74%) rename src/Pagination/{ZeroFirstPaginator.php => ZeroPerPagePaginator.php} (75%) diff --git a/src/Execution/ModelsLoader/PaginatedModelsLoader.php b/src/Execution/ModelsLoader/PaginatedModelsLoader.php index bd57b3ee9b..cf63986368 100644 --- a/src/Execution/ModelsLoader/PaginatedModelsLoader.php +++ b/src/Execution/ModelsLoader/PaginatedModelsLoader.php @@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Pagination\LengthAwarePaginator; use Nuwave\Lighthouse\Pagination\PaginationArgs; -use Nuwave\Lighthouse\Pagination\ZeroFirstLengthAwarePaginator; +use Nuwave\Lighthouse\Pagination\ZeroPerPageLengthAwarePaginator; use Nuwave\Lighthouse\Support\Utils; class PaginatedModelsLoader implements ModelsLoader @@ -185,7 +185,7 @@ protected function convertRelationToPaginator(EloquentCollection $parents): void $total = CountModelsLoader::extractCount($model, $this->relation); $paginator = $first === 0 - ? new ZeroFirstLengthAwarePaginator($total, $page) + ? new ZeroPerPageLengthAwarePaginator($total, $page) : new LengthAwarePaginator($model->getRelation($this->relation), $total, $first, $page); $model->setRelation($this->relation, $paginator); diff --git a/src/Pagination/PaginateDirective.php b/src/Pagination/PaginateDirective.php index e15080cfa0..876b044ec4 100644 --- a/src/Pagination/PaginateDirective.php +++ b/src/Pagination/PaginateDirective.php @@ -148,10 +148,10 @@ public function resolveField(FieldValue $fieldValue): callable if ($paginationArgs->first === 0) { if ($paginator instanceof LengthAwarePaginator) { - return new ZeroFirstLengthAwarePaginator($paginator->total(), $paginationArgs->page); + return new ZeroPerPageLengthAwarePaginator($paginator->total(), $paginationArgs->page); } - return new ZeroFirstPaginator($paginationArgs->page); + return new ZeroPerPagePaginator($paginationArgs->page); } return $paginator; diff --git a/src/Pagination/PaginationArgs.php b/src/Pagination/PaginationArgs.php index e998edf854..520fe33937 100644 --- a/src/Pagination/PaginationArgs.php +++ b/src/Pagination/PaginationArgs.php @@ -116,14 +116,14 @@ public function applyToBuilder(QueryBuilder|ScoutBuilder|EloquentBuilder|Relatio { if ($this->first === 0) { if ($this->type->isSimple()) { - return new ZeroFirstPaginator($this->page); + return new ZeroPerPagePaginator($this->page); } $total = $builder instanceof ScoutBuilder ? 0 // Laravel\Scout\Builder exposes no method to get the total count : $builder->count(); // @phpstan-ignore-line see Illuminate\Database\Query\Builder::count(), available as a mixin in the other classes - return new ZeroFirstLengthAwarePaginator($total, $this->page); + return new ZeroPerPageLengthAwarePaginator($total, $this->page); } $methodName = $this->type->isSimple() diff --git a/src/Pagination/ZeroFirstLengthAwarePaginator.php b/src/Pagination/ZeroPerPageLengthAwarePaginator.php similarity index 74% rename from src/Pagination/ZeroFirstLengthAwarePaginator.php rename to src/Pagination/ZeroPerPageLengthAwarePaginator.php index d3a216c54d..3f3b23b618 100644 --- a/src/Pagination/ZeroFirstLengthAwarePaginator.php +++ b/src/Pagination/ZeroPerPageLengthAwarePaginator.php @@ -5,10 +5,8 @@ use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; -/** - * @extends \Illuminate\Pagination\LengthAwarePaginator - */ -class ZeroFirstLengthAwarePaginator extends LengthAwarePaginator +/** @extends \Illuminate\Pagination\LengthAwarePaginator */ +class ZeroPerPageLengthAwarePaginator extends LengthAwarePaginator { public function __construct(int $total, int $page) { diff --git a/src/Pagination/ZeroFirstPaginator.php b/src/Pagination/ZeroPerPagePaginator.php similarity index 75% rename from src/Pagination/ZeroFirstPaginator.php rename to src/Pagination/ZeroPerPagePaginator.php index c5b270b702..8b69706f18 100644 --- a/src/Pagination/ZeroFirstPaginator.php +++ b/src/Pagination/ZeroPerPagePaginator.php @@ -5,10 +5,8 @@ use Illuminate\Pagination\Paginator; use Illuminate\Support\Collection; -/** - * @extends \Illuminate\Pagination\Paginator - */ -class ZeroFirstPaginator extends Paginator +/** @extends \Illuminate\Pagination\Paginator */ +class ZeroPerPagePaginator extends Paginator { public function __construct(int $page) { From 2b18ad05fbf3a2f32a8dcfd1c8521f058d734663 Mon Sep 17 00:00:00 2001 From: spawnia Date: Fri, 15 Dec 2023 12:45:28 +0000 Subject: [PATCH 6/6] Apply proto changes --- src/Tracing/FederatedTracing/Proto/FieldStat.php | 2 +- .../FederatedTracing/Proto/QueryLatencyStats.php | 2 +- src/Tracing/FederatedTracing/Proto/Trace.php | 10 +++++----- .../Proto/Trace/QueryPlanNode/FetchNode.php | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Tracing/FederatedTracing/Proto/FieldStat.php b/src/Tracing/FederatedTracing/Proto/FieldStat.php index fe55b37cf2..995f4f6466 100644 --- a/src/Tracing/FederatedTracing/Proto/FieldStat.php +++ b/src/Tracing/FederatedTracing/Proto/FieldStat.php @@ -89,7 +89,7 @@ class FieldStat extends \Google\Protobuf\Internal\Message * field_execution_weight). * @var int|string $observed_execution_count * Number of times that the resolver for this field is directly observed being - * executed + * executed. * @var int|string $estimated_execution_count * Same as `observed_execution_count` but potentially scaled upwards if the server was only * performing field-level instrumentation on a sampling of operations. For diff --git a/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php b/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php index cef9dd93f5..a62ca72476 100644 --- a/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php +++ b/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php @@ -106,7 +106,7 @@ class QueryLatencyStats extends \Google\Protobuf\Internal\Message * @var int|string $persisted_query_misses * @var array|array|\Google\Protobuf\Internal\RepeatedField $cache_latency_count * This array includes the latency buckets for all operations included in cache_hits - * See comment on latency_count for details + * See comment on latency_count for details. * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\PathErrorStats $root_error_stats * Paths and counts for each error. The total number of requests with errors within this object should be the same as * requests_with_errors_count below. diff --git a/src/Tracing/FederatedTracing/Proto/Trace.php b/src/Tracing/FederatedTracing/Proto/Trace.php index f8fe37ed32..a8fab65164 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace.php +++ b/src/Tracing/FederatedTracing/Proto/Trace.php @@ -170,15 +170,15 @@ class Trace extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var \Google\Protobuf\Timestamp $start_time - * Wallclock time when the trace began + * Wallclock time when the trace began. * @var \Google\Protobuf\Timestamp $end_time - * Wallclock time when the trace ended + * Wallclock time when the trace ended. * @var int|string $duration_ns * High precision duration of the trace; may not equal end_time-start_time - * (eg, if your machine's clock changed during the trace) + * (eg, if your machine's clock changed during the trace). * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Node $root * A tree containing information about all resolvers run directly by this - * service, including errors + * service, including errors. * @var bool $is_incomplete * If this is true, the trace is potentially missing some nodes that were * present on the query plan. This can happen if the trace span buffer used @@ -201,7 +201,7 @@ class Trace extends \Google\Protobuf\Internal\Message * @var string $unexecutedOperationBody * Optional: when GraphQL parsing or validation against the GraphQL schema fails, these fields * can include reference to the operation being sent for users to dig into the set of operations - * that are failing validation + * that are failing validation. * @var string $unexecutedOperationName * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Details $details * @var string $client_name diff --git a/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php b/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php index edc48c8653..f1a4a029fa 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php +++ b/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php @@ -66,12 +66,12 @@ class FetchNode extends \Google\Protobuf\Internal\Message * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace $trace * This Trace only contains start_time, end_time, duration_ns, and root; * all timings were calculated **on the subgraph**, and clock skew - * will be handled by the ingress server + * will be handled by the ingress server. * @var int|string $sent_time_offset - * relative to the outer trace's start_time, in ns, measured in the Router/Gateway + * relative to the outer trace's start_time, in ns, measured in the Router/Gateway. * @var \Google\Protobuf\Timestamp $sent_time * Wallclock times measured in the Router/Gateway for when this operation was - * sent and received + * sent and received. * @var \Google\Protobuf\Timestamp $received_time * } */