diff --git a/src/Resolver/QueryResult.php b/src/Resolver/QueryResult.php index 33f1bf2..01a06a1 100644 --- a/src/Resolver/QueryResult.php +++ b/src/Resolver/QueryResult.php @@ -40,26 +40,26 @@ public function __construct( $queryParams = $this->node->args()['queryParams'] ?? []; $limit = $queryParams['limit'] ?? null; $before = $queryParams['before'] ?? null; - - $queryBuilder = $this->query->builder(); - $rootEntity = $this->entityManager->findEntity(name: $this->node->unwrappedType()); - $cursorFieldNames = \array_keys( - \array_filter( - $rootEntity->reservedFields(), - static fn (string $reservedFieldType): bool => $reservedFieldType === 'Cursor' - ) - ); - - if ($queryBuilder->cursorOrderings() !== []) { - $queryBuilder->enableCursorProjection(); - } - - $cursorOrderings = $queryBuilder->cursorOrderings(); $batchKey = BatchKey(node: $this->node); + $queryBuilder = null; if (ResultBuffer()->has($batchKey)) { $batchResult = ResultBuffer()->get($batchKey); } else { + $queryBuilder = $this->query->builder(); + $rootEntity = $this->entityManager->findEntity(name: $this->node->unwrappedType()); + $cursorFieldNames = \array_keys( + \array_filter( + $rootEntity->reservedFields(), + static fn (string $reservedFieldType): bool => $reservedFieldType === 'Cursor' + ) + ); + + if ($queryBuilder->cursorOrderings() !== []) { + $queryBuilder->enableCursorProjection(); + } + + $cursorOrderings = $queryBuilder->cursorOrderings(); $doctrineQuery = $queryBuilder->getQuery(); // Nested collection pagination must happen per parent row, @@ -163,11 +163,11 @@ public function __construct( } // Filter the batch to only include results for the current node (only if node has a parent) - $filteredBatch = (function () use ($batchResult): array { + $filteredBatch = (function () use ($batchResult, $queryBuilder): array { if (! $this->node->isTopLevel()) { $parentEntity = $this->entityManager->findEntity(name: $this->node->unwrappedParentType()); - $parentAlias = $this->query->builder()->parentAlias(); + $parentAlias = ($queryBuilder ?? $this->entityManager->createQueryBuilder())->parentAlias(); // Extract this node's parent ID $parentId = $this->node->parentId(); diff --git a/tests/QueryResultTest.php b/tests/QueryResultTest.php new file mode 100644 index 0000000..4e3b587 --- /dev/null +++ b/tests/QueryResultTest.php @@ -0,0 +1,72 @@ +createMock(Node::class); + $node->method('args')->willReturn([]); + $node->method('name')->willReturn('children'); + $node->method('unwrappedParentType')->willReturn('Parent'); + $node->method('isTopLevel')->willReturn(false); + $node->method('isACollection')->willReturn(true); + $node->method('parentId')->willReturn(['id' => 1]); + + $query = $this->createMock(Query::class); + $query->method('isWorkable')->willReturn(true); + $query->expects(self::never())->method('builder'); + + $parentEntity = $this->createMock(Entity::class); + $parentEntity->method('idFieldNames')->willReturn(['id']); + $parentEntity->method('associationFieldNames')->willReturn([]); + + $queryBuilder = $this->createMock(QueryBuilder::class); + $queryBuilder->method('parentAlias')->willReturn('__parent'); + + $entityManager = $this->createMock(EntityManager::class); + $entityManager->method('findEntity')->willReturn($parentEntity); + $entityManager->method('createQueryBuilder')->willReturn($queryBuilder); + $entityManager->method('identifiersMatch')->willReturnCallback( + static fn (mixed $left, mixed $right): bool => (string) $left === (string) $right + ); + + $expected = [['id' => 10, '__parent_id' => 1]]; + ResultBuffer()->add(BatchKey($node), $expected); + + $result = new class(query: $query, node: $node, entityManager: $entityManager) implements Result + { + use QueryResult; + }; + $deferred = $result->value(); + + self::assertInstanceOf(Deferred::class, $deferred); + + SyncPromiseQueue::run(); + + self::assertSame(SyncPromise::FULFILLED, $deferred->state); + self::assertSame($expected, $deferred->result); + + NodeBuffer()->clear(); + ResultBuffer()->clear(); + } +}