1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace DutchCodingCompany\CursorPagination\Pagination; |
4
|
|
|
|
5
|
|
|
use GraphQL\Language\AST\FieldDefinitionNode; |
6
|
|
|
use GraphQL\Language\AST\ObjectTypeDefinitionNode; |
7
|
|
|
use Nuwave\Lighthouse\Pagination\PaginationType; |
8
|
|
|
use Nuwave\Lighthouse\Schema\AST\ASTHelper; |
9
|
|
|
use Nuwave\Lighthouse\Schema\AST\PartialParser; |
10
|
|
|
|
11
|
|
|
class PaginationManipulator extends \Nuwave\Lighthouse\Pagination\PaginationManipulator |
12
|
|
|
{ |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Transform the definition for a field to a field with pagination. |
16
|
|
|
* |
17
|
|
|
* This makes either an offset-based Paginator or a cursor-based Connection. |
18
|
|
|
* The types in between are automatically generated and applied to the schema. |
19
|
|
|
* |
20
|
|
|
* @param \Nuwave\Lighthouse\Pagination\PaginationType $paginationType |
21
|
|
|
*/ |
22
|
|
|
public function transformToPaginatedField( |
23
|
|
|
PaginationType $paginationType, |
24
|
|
|
FieldDefinitionNode &$fieldDefinition, |
25
|
|
|
ObjectTypeDefinitionNode &$parentType, |
26
|
|
|
?int $defaultCount = null, |
27
|
|
|
?int $maxCount = null, |
28
|
|
|
?ObjectTypeDefinitionNode $edgeType = null |
29
|
|
|
): void { |
30
|
|
|
if ($paginationType->isConnection()) { |
31
|
|
|
$this->registerConnection($fieldDefinition, $parentType, $defaultCount, $maxCount, $edgeType); |
32
|
|
|
} else { |
33
|
|
|
$this->registerPaginator($fieldDefinition, $parentType, $defaultCount, $maxCount); |
34
|
|
|
} |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Register connection with schema. |
39
|
|
|
*/ |
40
|
|
|
protected function registerConnection( |
41
|
|
|
FieldDefinitionNode &$fieldDefinition, |
42
|
|
|
ObjectTypeDefinitionNode &$parentType, |
43
|
|
|
?int $defaultCount = null, |
44
|
|
|
?int $maxCount = null, |
45
|
|
|
?ObjectTypeDefinitionNode $edgeType = null |
46
|
|
|
): void { |
47
|
|
|
// Register cursor specific pagination type |
48
|
|
|
$this->addCursorPaginationInfoType(); |
49
|
|
|
|
50
|
|
|
$fieldTypeName = ASTHelper::getUnderlyingTypeName($fieldDefinition); |
51
|
|
|
|
52
|
|
|
if ($edgeType) { |
53
|
|
|
$connectionEdgeName = $edgeType->name->value; |
54
|
|
|
$connectionTypeName = "{$connectionEdgeName}Connection"; |
55
|
|
|
} else { |
56
|
|
|
$connectionEdgeName = "{$fieldTypeName}Edge"; |
57
|
|
|
$connectionTypeName = "{$fieldTypeName}Connection"; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
$connectionFieldName = addslashes(ConnectionField::class); |
61
|
|
|
$connectionType = PartialParser::objectTypeDefinition(/** @lang GraphQL */ <<<GRAPHQL |
62
|
|
|
"A paginated list of $fieldTypeName edges." |
63
|
|
|
type $connectionTypeName { |
64
|
|
|
"Pagination information about the list of edges." |
65
|
|
|
pageInfo: CursorPageInfo! @field(resolver: "{$connectionFieldName}@pageInfoResolver") |
66
|
|
|
|
67
|
|
|
"A list of $fieldTypeName edges." |
68
|
|
|
edges: [$connectionEdgeName] @field(resolver: "{$connectionFieldName}@edgeResolver") |
69
|
|
|
} |
70
|
|
|
GRAPHQL |
71
|
|
|
); |
72
|
|
|
$this->addPaginationWrapperType($connectionType); |
73
|
|
|
|
74
|
|
|
$connectionEdge = $edgeType |
75
|
|
|
?? $this->documentAST->types[$connectionEdgeName] |
76
|
|
|
?? PartialParser::objectTypeDefinition(/** @lang GraphQL */ <<<GRAPHQL |
77
|
|
|
"An edge that contains a node of type $fieldTypeName and a cursor." |
78
|
|
|
type $connectionEdgeName { |
79
|
|
|
"The $fieldTypeName node." |
80
|
|
|
node: $fieldTypeName |
81
|
|
|
} |
82
|
|
|
GRAPHQL |
83
|
|
|
); |
84
|
|
|
$this->documentAST->setTypeDefinition($connectionEdge); |
85
|
|
|
|
86
|
|
|
$inputValueDefinitions = [ |
87
|
|
|
self::countArgument('first', $defaultCount, $maxCount), |
88
|
|
|
"\"A cursor after which elements are returned.\"\nafter: String", |
89
|
|
|
]; |
90
|
|
|
|
91
|
|
|
$connectionArguments = PartialParser::inputValueDefinitions($inputValueDefinitions); |
92
|
|
|
|
93
|
|
|
$fieldDefinition->arguments = ASTHelper::mergeNodeList($fieldDefinition->arguments, $connectionArguments); |
94
|
|
|
$fieldDefinition->type = PartialParser::namedType($connectionTypeName); |
95
|
|
|
$parentType->fields = ASTHelper::mergeNodeList($parentType->fields, [$fieldDefinition]); |
|
|
|
|
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Add the types required for pagination. |
100
|
|
|
*/ |
101
|
|
|
protected function addCursorPaginationInfoType(): void |
102
|
|
|
{ |
103
|
|
|
$this->documentAST->setTypeDefinition( |
104
|
|
|
PartialParser::objectTypeDefinition(/** @lang GraphQL */ ' |
105
|
|
|
"Pagination information about the corresponding list of items." |
106
|
|
|
type CursorPageInfo { |
107
|
|
|
"When paginating forwards, the cursor to continue." |
108
|
|
|
after: String |
109
|
|
|
|
110
|
|
|
"When paginating forwards, are there more items?" |
111
|
|
|
hasAfter: Boolean! |
112
|
|
|
|
113
|
|
|
"Total number of node in connection." |
114
|
|
|
total: Int |
115
|
|
|
|
116
|
|
|
"Count of nodes in current request." |
117
|
|
|
count: Int |
118
|
|
|
|
119
|
|
|
} |
120
|
|
|
') |
121
|
|
|
); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..