15
15
16
16
use ApiPlatform \Core \Api \IdentifiersExtractorInterface ;
17
17
use ApiPlatform \Core \Bridge \Graphql \Resolver \CollectionResolverFactoryInterface ;
18
+ use ApiPlatform \Core \Bridge \Graphql \Resolver \ItemMutationResolverFactoryInterface ;
18
19
use ApiPlatform \Core \Bridge \Graphql \Resolver \ItemResolverFactoryInterface ;
19
20
use ApiPlatform \Core \Exception \ResourceClassNotFoundException ;
20
21
use ApiPlatform \Core \Metadata \Property \Factory \PropertyMetadataFactoryInterface ;
@@ -48,35 +49,43 @@ final class SchemaBuilder implements SchemaBuilderInterface
48
49
private $ resourceMetadataFactory ;
49
50
private $ collectionResolverFactory ;
50
51
private $ itemResolverFactory ;
52
+ private $ itemMutationResolverFactory ;
51
53
private $ identifiersExtractor ;
52
54
private $ paginationEnabled ;
53
55
private $ resourceTypesCache = [];
54
56
55
- public function __construct (PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , ResourceNameCollectionFactoryInterface $ resourceNameCollectionFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , CollectionResolverFactoryInterface $ collectionResolverFactory , ItemResolverFactoryInterface $ itemResolverFactory , IdentifiersExtractorInterface $ identifiersExtractor , bool $ paginationEnabled )
57
+ public function __construct (PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , ResourceNameCollectionFactoryInterface $ resourceNameCollectionFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , CollectionResolverFactoryInterface $ collectionResolverFactory , ItemResolverFactoryInterface $ itemResolverFactory , ItemMutationResolverFactoryInterface $ itemMutationResolverFactory , IdentifiersExtractorInterface $ identifiersExtractor , bool $ paginationEnabled )
56
58
{
57
59
$ this ->propertyNameCollectionFactory = $ propertyNameCollectionFactory ;
58
60
$ this ->propertyMetadataFactory = $ propertyMetadataFactory ;
59
61
$ this ->resourceNameCollectionFactory = $ resourceNameCollectionFactory ;
60
62
$ this ->resourceMetadataFactory = $ resourceMetadataFactory ;
61
63
$ this ->collectionResolverFactory = $ collectionResolverFactory ;
62
64
$ this ->itemResolverFactory = $ itemResolverFactory ;
65
+ $ this ->itemMutationResolverFactory = $ itemMutationResolverFactory ;
63
66
$ this ->identifiersExtractor = $ identifiersExtractor ;
64
67
$ this ->paginationEnabled = $ paginationEnabled ;
65
68
}
66
69
67
70
public function getSchema (): Schema
68
71
{
69
72
$ queryFields = [];
73
+ $ mutationFields = [];
70
74
71
75
foreach ($ this ->resourceNameCollectionFactory ->create () as $ resource ) {
72
76
$ queryFields += $ this ->getQueryFields ($ resource );
77
+ $ mutationFields += $ this ->getMutationFields ($ resource );
73
78
}
74
79
75
80
return new Schema ([
76
81
'query ' => new ObjectType ([
77
82
'name ' => 'Query ' ,
78
83
'fields ' => $ queryFields ,
79
84
]),
85
+ 'mutation ' => new ObjectType ([
86
+ 'name ' => 'Mutation ' ,
87
+ 'fields ' => $ mutationFields ,
88
+ ]),
80
89
]);
81
90
}
82
91
@@ -107,23 +116,44 @@ private function getQueryFields(string $resource): array
107
116
return $ queryFields ;
108
117
}
109
118
119
+ /**
120
+ * Gets the mutation fields of the schema.
121
+ */
122
+ private function getMutationFields (string $ resource ): array
123
+ {
124
+ $ mutationFields = [];
125
+ $ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ resource );
126
+ $ shortName = $ resourceMetadata ->getShortName ();
127
+ $ resourceType = new Type (Type::BUILTIN_TYPE_OBJECT , true , $ resource );
128
+
129
+ foreach ($ this ->getOperations ($ resourceMetadata , false , true ) as $ operationName => $ mutationItemOperation ) {
130
+ if ($ fieldConfiguration = $ this ->getResourceFieldConfiguration (ucfirst ("{$ operationName }s one $ shortName. " ), $ resourceType , $ resource , $ operationName , false , true )) {
131
+ $ fieldConfiguration ['args ' ] += $ this ->getResourceMutationArgumentsConfiguration ($ resourceType , $ resource , $ operationName );
132
+ $ fieldConfiguration ['resolve ' ] = $ resourceType ->isCollection () ? null : $ this ->itemMutationResolverFactory ->createItemMutationResolver ($ resource , $ operationName );
133
+ $ mutationFields [$ operationName .$ shortName ] = $ fieldConfiguration ;
134
+ }
135
+ }
136
+
137
+ return $ mutationFields ;
138
+ }
139
+
110
140
/**
111
141
* Get the field configuration of a resource.
112
142
*
113
143
* @see http://webonyx.github.io/graphql-php/type-system/object-types/
114
144
*
115
145
* @return array|null
116
146
*/
117
- private function getResourceFieldConfiguration (string $ fieldDescription = null , Type $ type , string $ rootResource , string $ operationName , bool $ isInput = false )
147
+ private function getResourceFieldConfiguration (string $ fieldDescription = null , Type $ type , string $ rootResource , string $ operationName , bool $ isInput = false , bool $ isMutation = false )
118
148
{
119
149
try {
120
- $ graphqlType = $ this ->convertType ($ type , $ operationName , $ isInput );
150
+ $ graphqlType = $ this ->convertType ($ type , $ operationName , $ isInput, $ isMutation );
121
151
$ graphqlWrappedType = $ graphqlType instanceof WrappingType ? $ graphqlType ->getWrappedType () : $ graphqlType ;
122
152
$ isInternalGraphqlType = in_array ($ graphqlWrappedType , GraphQLType::getInternalTypes (), true );
123
153
$ className = $ isInternalGraphqlType ? '' : ($ type ->isCollection () ? $ type ->getCollectionValueType ()->getClassName () : $ type ->getClassName ());
124
154
125
155
$ args = [];
126
- if ($ this ->paginationEnabled && !$ isInternalGraphqlType && $ type ->isCollection () && !$ isInput ) {
156
+ if ($ this ->paginationEnabled && !$ isInternalGraphqlType && $ type ->isCollection () && !$ isInput && ! $ isMutation ) {
127
157
$ args = [
128
158
'first ' => [
129
159
'type ' => GraphQLType::int (),
@@ -136,7 +166,7 @@ private function getResourceFieldConfiguration(string $fieldDescription = null,
136
166
];
137
167
}
138
168
139
- if ($ isInternalGraphqlType || $ isInput ) {
169
+ if ($ isInternalGraphqlType || $ isInput || $ isMutation ) {
140
170
$ resolve = null ;
141
171
} else {
142
172
$ resolve = $ type ->isCollection () ? $ this ->collectionResolverFactory ->createCollectionResolver ($ className , $ rootResource , $ operationName ) : $ this ->itemResolverFactory ->createItemResolver ($ className , $ rootResource , $ operationName );
@@ -153,6 +183,14 @@ private function getResourceFieldConfiguration(string $fieldDescription = null,
153
183
}
154
184
}
155
185
186
+ /**
187
+ * Gets the field arguments of the mutation of a given resource.
188
+ */
189
+ private function getResourceMutationArgumentsConfiguration (Type $ type , string $ resource , string $ operationName ): array
190
+ {
191
+ return ['input ' => $ this ->getResourceFieldConfiguration (null , $ type , $ resource , $ operationName , true , true )];
192
+ }
193
+
156
194
/**
157
195
* Gets the field arguments of the identifier of a given resource.
158
196
*
@@ -183,7 +221,7 @@ private function getResourceIdentifiersArgumentsConfiguration(string $resource,
183
221
*
184
222
* @throws InvalidTypeException
185
223
*/
186
- private function convertType (Type $ type , string $ operationName , bool $ isInput = false ): GraphQLType
224
+ private function convertType (Type $ type , string $ operationName , bool $ isInput = false , bool $ isMutation = false ): GraphQLType
187
225
{
188
226
switch ($ type ->getBuiltinType ()) {
189
227
case Type::BUILTIN_TYPE_BOOL :
@@ -211,7 +249,7 @@ private function convertType(Type $type, string $operationName, bool $isInput =
211
249
throw new InvalidTypeException ();
212
250
}
213
251
214
- $ graphqlType = $ this ->getResourceObjectType ($ className , $ resourceMetadata , $ operationName , $ isInput );
252
+ $ graphqlType = $ this ->getResourceObjectType ($ className , $ resourceMetadata , $ operationName , $ isInput, $ isMutation );
215
253
break ;
216
254
default :
217
255
throw new InvalidTypeException ();
@@ -229,9 +267,9 @@ private function convertType(Type $type, string $operationName, bool $isInput =
229
267
*
230
268
* @return ObjectType|InputObjectType
231
269
*/
232
- private function getResourceObjectType (string $ resource , ResourceMetadata $ resourceMetadata , string $ operationName , bool $ isInput = false )
270
+ private function getResourceObjectType (string $ resource , ResourceMetadata $ resourceMetadata , string $ operationName , bool $ isInput = false , bool $ isMutation = false )
233
271
{
234
- $ shortName = $ resourceMetadata ->getShortName ().($ isInput ? 'Input ' : '' );
272
+ $ shortName = $ resourceMetadata ->getShortName ().($ isInput ? 'Input ' : '' ).( $ isMutation ? ucfirst ( $ operationName ). " Mutation " : '' ) ;
235
273
236
274
if (isset ($ this ->resourceTypesCache [$ shortName ])) {
237
275
return $ this ->resourceTypesCache [$ shortName ];
@@ -240,8 +278,8 @@ private function getResourceObjectType(string $resource, ResourceMetadata $resou
240
278
$ configuration = [
241
279
'name ' => $ shortName ,
242
280
'description ' => $ resourceMetadata ->getDescription (),
243
- 'fields ' => function () use ($ resource , $ operationName , $ isInput ) {
244
- return $ this ->getResourceObjectTypeFields ($ resource , $ operationName , $ isInput );
281
+ 'fields ' => function () use ($ resource , $ operationName , $ isInput, $ isMutation ) {
282
+ return $ this ->getResourceObjectTypeFields ($ resource , $ operationName , $ isInput, $ isMutation );
245
283
},
246
284
];
247
285
@@ -251,18 +289,19 @@ private function getResourceObjectType(string $resource, ResourceMetadata $resou
251
289
/**
252
290
* Gets the fields of the type of the given resource.
253
291
*/
254
- private function getResourceObjectTypeFields (string $ resource , string $ operationName , bool $ isInput = false ): array
292
+ private function getResourceObjectTypeFields (string $ resource , string $ operationName , bool $ isInput = false , bool $ isMutation = false ): array
255
293
{
256
294
$ fields = [];
257
295
258
296
foreach ($ this ->propertyNameCollectionFactory ->create ($ resource ) as $ property ) {
259
297
$ propertyMetadata = $ this ->propertyMetadataFactory ->create ($ resource , $ property );
260
298
if (null === ($ propertyType = $ propertyMetadata ->getType ())
261
- || !$ propertyMetadata ->isReadable ()) {
299
+ || !$ propertyMetadata ->isReadable ()
300
+ || ($ isMutation && 'delete ' === $ operationName && !$ propertyMetadata ->isIdentifier ())) {
262
301
continue ;
263
302
}
264
303
265
- if ($ fieldConfiguration = $ this ->getResourceFieldConfiguration ($ propertyMetadata ->getDescription (), $ propertyType , $ resource , $ operationName , $ isInput )) {
304
+ if ($ fieldConfiguration = $ this ->getResourceFieldConfiguration ($ propertyMetadata ->getDescription (), $ propertyType , $ resource , $ operationName , $ isInput, $ isMutation )) {
266
305
$ fields [$ property ] = $ fieldConfiguration ;
267
306
}
268
307
}
@@ -335,6 +374,10 @@ private function getOperations(ResourceMetadata $resourceMetadata, bool $isQuery
335
374
continue ;
336
375
}
337
376
377
+ if (Request::METHOD_PATCH === $ operation ['method ' ]) {
378
+ continue ;
379
+ }
380
+
338
381
yield $ operationName => $ operation ;
339
382
}
340
383
}
0 commit comments