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 ;
@@ -47,39 +48,53 @@ final class SchemaBuilder implements SchemaBuilderInterface
47
48
private $ resourceMetadataFactory ;
48
49
private $ collectionResolverFactory ;
49
50
private $ itemResolverFactory ;
51
+ private $ itemMutationResolverFactory ;
50
52
private $ identifiersExtractor ;
51
53
private $ paginationEnabled ;
52
54
private $ resourceTypesCache = [];
53
55
54
- public function __construct (PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , ResourceNameCollectionFactoryInterface $ resourceNameCollectionFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , CollectionResolverFactoryInterface $ collectionResolverFactory , ItemResolverFactoryInterface $ itemResolverFactory , IdentifiersExtractorInterface $ identifiersExtractor , bool $ paginationEnabled )
56
+ public function __construct (PropertyNameCollectionFactoryInterface $ propertyNameCollectionFactory , PropertyMetadataFactoryInterface $ propertyMetadataFactory , ResourceNameCollectionFactoryInterface $ resourceNameCollectionFactory , ResourceMetadataFactoryInterface $ resourceMetadataFactory , CollectionResolverFactoryInterface $ collectionResolverFactory , ItemResolverFactoryInterface $ itemResolverFactory , ItemMutationResolverFactoryInterface $ itemMutationResolverFactory , IdentifiersExtractorInterface $ identifiersExtractor , bool $ paginationEnabled )
55
57
{
56
58
$ this ->propertyNameCollectionFactory = $ propertyNameCollectionFactory ;
57
59
$ this ->propertyMetadataFactory = $ propertyMetadataFactory ;
58
60
$ this ->resourceNameCollectionFactory = $ resourceNameCollectionFactory ;
59
61
$ this ->resourceMetadataFactory = $ resourceMetadataFactory ;
60
62
$ this ->collectionResolverFactory = $ collectionResolverFactory ;
61
63
$ this ->itemResolverFactory = $ itemResolverFactory ;
64
+ $ this ->itemMutationResolverFactory = $ itemMutationResolverFactory ;
62
65
$ this ->identifiersExtractor = $ identifiersExtractor ;
63
66
$ this ->paginationEnabled = $ paginationEnabled ;
64
67
}
65
68
66
69
public function getSchema (): Schema
67
70
{
68
71
$ queryFields = [];
72
+ $ mutationFields = [];
73
+
69
74
foreach ($ this ->resourceNameCollectionFactory ->create () as $ resourceClass ) {
70
75
$ resourceMetadata = $ this ->resourceMetadataFactory ->create ($ resourceClass );
71
- if (!isset ($ resourceMetadata ->getGraphql ()['query ' ])) {
72
- continue ;
73
- }
76
+ $ graphqlConfiguration = $ resourceMetadata ->getGraphql () ?? [];
74
77
75
- $ queryFields += $ this ->getQueryFields ($ resourceClass , $ resourceMetadata );
78
+ foreach ($ graphqlConfiguration as $ operationName => $ value ) {
79
+ if ('query ' === $ operationName ) {
80
+ $ queryFields += $ this ->getQueryFields ($ resourceClass , $ resourceMetadata );
81
+
82
+ continue ;
83
+ }
84
+
85
+ $ mutationFields [$ operationName .$ resourceMetadata ->getShortName ()] = $ this ->getMutationFields ($ resourceClass , $ resourceMetadata , $ operationName );
86
+ }
76
87
}
77
88
78
89
return new Schema ([
79
90
'query ' => new ObjectType ([
80
91
'name ' => 'Query ' ,
81
92
'fields ' => $ queryFields ,
82
93
]),
94
+ 'mutation ' => new ObjectType ([
95
+ 'name ' => 'Mutation ' ,
96
+ 'fields ' => $ mutationFields ,
97
+ ]),
83
98
]);
84
99
}
85
100
@@ -104,16 +119,32 @@ private function getQueryFields(string $resourceClass, ResourceMetadata $resourc
104
119
}
105
120
106
121
/**
107
- * Gets the field configuration of a resource.
122
+ * Gets the mutation field for the given operation name.
123
+ */
124
+ private function getMutationFields (string $ resourceClass , ResourceMetadata $ resourceMetadata , string $ mutationName ): array
125
+ {
126
+ $ shortName = $ resourceMetadata ->getShortName ();
127
+ $ resourceType = new Type (Type::BUILTIN_TYPE_OBJECT , true , $ resourceClass );
128
+
129
+ if ($ fieldConfiguration = $ this ->getResourceFieldConfiguration (ucfirst ("{$ mutationName }s a $ shortName. " ), $ resourceType , $ resourceClass , false , true , $ mutationName )) {
130
+ $ fieldConfiguration ['args ' ] += ['input ' => $ this ->getResourceFieldConfiguration (null , $ resourceType , $ resourceClass , true , true , $ mutationName )];
131
+ $ fieldConfiguration ['resolve ' ] = $ resourceType ->isCollection () ? null : $ this ->itemMutationResolverFactory ->createItemMutationResolver ($ resourceClass , $ mutationName );
132
+ }
133
+
134
+ return $ fieldConfiguration ;
135
+ }
136
+
137
+ /**
138
+ * Get the field configuration of a resource.
108
139
*
109
140
* @see http://webonyx.github.io/graphql-php/type-system/object-types/
110
141
*
111
142
* @return array|null
112
143
*/
113
- private function getResourceFieldConfiguration (string $ fieldDescription = null , Type $ type , string $ rootResource , bool $ isInput = false )
144
+ private function getResourceFieldConfiguration (string $ fieldDescription = null , Type $ type , string $ rootResource , bool $ isInput = false , bool $ isMutation = false , string $ mutationName = null )
114
145
{
115
146
try {
116
- $ graphqlType = $ this ->convertType ($ type , $ isInput );
147
+ $ graphqlType = $ this ->convertType ($ type , $ isInput, $ isMutation , $ mutationName );
117
148
$ graphqlWrappedType = $ graphqlType instanceof WrappingType ? $ graphqlType ->getWrappedType () : $ graphqlType ;
118
149
$ isInternalGraphqlType = in_array ($ graphqlWrappedType , GraphQLType::getInternalTypes (), true );
119
150
if ($ isInternalGraphqlType ) {
@@ -123,7 +154,7 @@ private function getResourceFieldConfiguration(string $fieldDescription = null,
123
154
}
124
155
125
156
$ args = [];
126
- if ($ this ->paginationEnabled && !$ isInternalGraphqlType && $ type ->isCollection () && !$ isInput ) {
157
+ if ($ this ->paginationEnabled && !$ isInternalGraphqlType && $ type ->isCollection () && !$ isInput && ! $ isMutation ) {
127
158
$ args = [
128
159
'first ' => [
129
160
'type ' => GraphQLType::int (),
@@ -136,7 +167,7 @@ private function getResourceFieldConfiguration(string $fieldDescription = null,
136
167
];
137
168
}
138
169
139
- if ($ isInternalGraphqlType || $ isInput ) {
170
+ if ($ isInternalGraphqlType || $ isInput || $ isMutation ) {
140
171
$ resolve = null ;
141
172
} else {
142
173
$ resolve = $ type ->isCollection () ? $ this ->collectionResolverFactory ->createCollectionResolver ($ className , $ rootResource ) : $ this ->itemResolverFactory ->createItemResolver ($ className , $ rootResource );
@@ -183,7 +214,7 @@ private function getResourceIdentifiersArgumentsConfiguration(string $resourceCl
183
214
*
184
215
* @throws InvalidTypeException
185
216
*/
186
- private function convertType (Type $ type , bool $ isInput = false ): GraphQLType
217
+ private function convertType (Type $ type , bool $ isInput = false , bool $ isMutation = false , string $ mutationName = null ): GraphQLType
187
218
{
188
219
switch ($ type ->getBuiltinType ()) {
189
220
case Type::BUILTIN_TYPE_BOOL :
@@ -211,7 +242,7 @@ private function convertType(Type $type, bool $isInput = false): GraphQLType
211
242
throw new InvalidTypeException ();
212
243
}
213
244
214
- $ graphqlType = $ this ->getResourceObjectType ($ className , $ resourceMetadata , $ isInput );
245
+ $ graphqlType = $ this ->getResourceObjectType ($ className , $ resourceMetadata , $ isInput, $ isMutation , $ mutationName );
215
246
break ;
216
247
default :
217
248
throw new InvalidTypeException ();
@@ -229,18 +260,19 @@ private function convertType(Type $type, bool $isInput = false): GraphQLType
229
260
*
230
261
* @return ObjectType|InputObjectType
231
262
*/
232
- private function getResourceObjectType (string $ resource , ResourceMetadata $ resourceMetadata , bool $ isInput = false )
263
+ private function getResourceObjectType (string $ resource , ResourceMetadata $ resourceMetadata , bool $ isInput = false , bool $ isMutation = false , string $ mutationName = null )
233
264
{
234
- $ shortName = $ resourceMetadata ->getShortName ().($ isInput ? 'Input ' : '' );
265
+ $ shortName = $ resourceMetadata ->getShortName ().($ isInput ? 'Input ' : '' ).($ isMutation ? ucfirst ($ mutationName ).'Mutation ' : '' );
266
+
235
267
if (isset ($ this ->resourceTypesCache [$ shortName ])) {
236
268
return $ this ->resourceTypesCache [$ shortName ];
237
269
}
238
270
239
271
$ configuration = [
240
272
'name ' => $ shortName ,
241
273
'description ' => $ resourceMetadata ->getDescription (),
242
- 'fields ' => function () use ($ resource , $ isInput ) {
243
- return $ this ->getResourceObjectTypeFields ($ resource , $ isInput );
274
+ 'fields ' => function () use ($ resource , $ isInput, $ isMutation , $ mutationName ) {
275
+ return $ this ->getResourceObjectTypeFields ($ resource , $ isInput, $ isMutation , $ mutationName );
244
276
},
245
277
];
246
278
@@ -250,16 +282,18 @@ private function getResourceObjectType(string $resource, ResourceMetadata $resou
250
282
/**
251
283
* Gets the fields of the type of the given resource.
252
284
*/
253
- private function getResourceObjectTypeFields (string $ resource , bool $ isInput = false ): array
285
+ private function getResourceObjectTypeFields (string $ resource , bool $ isInput = false , bool $ isMutation = false , string $ mutationName = null ): array
254
286
{
255
287
$ fields = [];
256
288
foreach ($ this ->propertyNameCollectionFactory ->create ($ resource ) as $ property ) {
257
289
$ propertyMetadata = $ this ->propertyMetadataFactory ->create ($ resource , $ property );
258
- if (null === ($ propertyType = $ propertyMetadata ->getType ()) || !$ propertyMetadata ->isReadable ()) {
290
+ if (null === ($ propertyType = $ propertyMetadata ->getType ())
291
+ || !$ propertyMetadata ->isReadable ()
292
+ || ($ isMutation && 'delete ' === $ mutationName && !$ propertyMetadata ->isIdentifier ())) {
259
293
continue ;
260
294
}
261
295
262
- if ($ fieldConfiguration = $ this ->getResourceFieldConfiguration ($ propertyMetadata ->getDescription (), $ propertyType , $ resource , $ isInput )) {
296
+ if ($ fieldConfiguration = $ this ->getResourceFieldConfiguration ($ propertyMetadata ->getDescription (), $ propertyType , $ resource , $ isInput, $ isMutation , $ mutationName )) {
263
297
$ fields [$ property ] = $ fieldConfiguration ;
264
298
}
265
299
}
0 commit comments