diff --git a/docs/annotations/annotations-reference.md b/docs/annotations/annotations-reference.md index 882a6e5ae..8f4a5feff 100644 --- a/docs/annotations/annotations-reference.md +++ b/docs/annotations/annotations-reference.md @@ -386,7 +386,7 @@ The class exposing the mutation(s) must be declared as a [service](https://symfo Optional attributes: -- **targetType** : The GraphQL type to attach the field to. It must be a mutation. (by default, it'll be the root Mutation type of the default schema. see [Default Schema](../definitions/schema.md#default-schema)). +- **targetType** : The GraphQL type(s) to attach the field to. It must be a mutation. (by default, it'll be the root Mutation type of the default schema. see [Default Schema](../definitions/schema.md#default-schema)). You can specify one or multiple target types. Example: @@ -422,7 +422,7 @@ class MutationProvider { This annotation applies on classes to indicate that it contains methods tagged with `@Query` or `@Mutation`. Without it, the `@Query` and `@Mutation` are ignored. When used, **remember to have a corresponding service with the fully qualified name of the class as service id**. -You can use `@Access` and/or `@IsPublic` on a provider class to add default access or visibility on defined query or mutation. +You can use `@Access` and/or `@IsPublic` on a provider class to add default access or visibility on defined query or mutation. Optional attributes: @@ -436,7 +436,7 @@ The class exposing the query(ies) must be declared as a [service](https://symfon Optional attributes: -- **targetType** : The GraphQL type to attach the field to (by default, it'll be the root Query type of the default schema. see [Default Schema](../definitions/schema.md#default-schema)). +- **targetType** : The GraphQL type(s) to attach the field to (by default, it'll be the root Query type of the default schema. see [Default Schema](../definitions/schema.md#default-schema)). You can specify one or multiple target types. Example: @@ -589,19 +589,18 @@ class Pet { } ``` - ## @Relay\Connection -This annotation extends the `@Type` annotation so it uses the same attributes. +This annotation extends the `@Type` annotation so it uses the same attributes. It prepends the `RelayConnectionFieldsBuilder` to the list of fields builders. The extra attributes are : - **edge** : The GraphQL type of the connection's edges -- **node** : The GraphQL type of the node of the connection's edges' +- **node** : The GraphQL type of the node of the connection's edges' You must define one and only one of this attributes. -If the `edge` attribute is used, the declaration is the same as adding a `RelayConnectionFieldsBuilder` +If the `edge` attribute is used, the declaration is the same as adding a `RelayConnectionFieldsBuilder` ```php */ public $targetType; } diff --git a/src/Annotation/Query.php b/src/Annotation/Query.php index a7d38d0f9..a50c5ffcd 100644 --- a/src/Annotation/Query.php +++ b/src/Annotation/Query.php @@ -13,9 +13,9 @@ final class Query extends Field { /** - * The target type to attach this query to. + * The target types to attach this query to. * - * @var string + * @var array */ public $targetType; } diff --git a/src/Config/Parser/AnnotationParser.php b/src/Config/Parser/AnnotationParser.php index 746e51936..17799c2aa 100644 --- a/src/Config/Parser/AnnotationParser.php +++ b/src/Config/Parser/AnnotationParser.php @@ -674,19 +674,28 @@ private static function getGraphQLFieldsFromProviders(GraphClass $graphClass, st continue; } - $annotationTarget = $annotation->targetType; - if (!$annotationTarget && $isDefaultTarget) { - $annotationTarget = $targetType; - if (!($annotation instanceof $expectedAnnotation)) { + $annotationTargets = $annotation->targetType; + + if (null === $annotationTargets) { + if ($isDefaultTarget) { + $annotationTargets = [$targetType]; + if (!$annotation instanceof $expectedAnnotation) { + continue; + } + } else { continue; } } - if ($annotationTarget !== $targetType) { + if (is_string($annotationTargets)) { + $annotationTargets = [$annotationTargets]; + } + + if (!in_array($targetType, $annotationTargets)) { continue; } - if (!($annotation instanceof $expectedAnnotation)) { + if (!$annotation instanceof $expectedAnnotation) { if (GQL\Mutation::class == $expectedAnnotation) { $message = sprintf('The provider "%s" try to add a query field on type "%s" (through @Query on method "%s") but "%s" is a mutation.', $providerMetadata->getName(), $targetType, $method->getName(), $targetType); } else { diff --git a/tests/Config/Parser/AnnotationParserTest.php b/tests/Config/Parser/AnnotationParserTest.php index 0c3d40f89..f0235757e 100644 --- a/tests/Config/Parser/AnnotationParserTest.php +++ b/tests/Config/Parser/AnnotationParserTest.php @@ -111,6 +111,12 @@ public function testTypes(): void 'access' => '@=override_access', 'public' => '@=default_public', ], + 'planet_armorResistance' => [ + 'type' => 'Int!', + 'resolve' => '@=call(service(\'Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository\').getArmorResistance, arguments({}, args))', + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); @@ -228,6 +234,12 @@ public function testInterfaceAutoguessed(): void 'fields' => [ 'name' => ['type' => 'String!', 'description' => 'The name of the character'], 'friends' => ['type' => '[Character]', 'description' => 'The friends of the character', 'resolve' => "@=resolver('App\\\\MyResolver::getFriends')"], + 'planet_armorResistance' => [ + 'type' => 'Int!', + 'resolve' => '@=call(service(\'Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository\').getArmorResistance, arguments({}, args))', + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); } @@ -253,6 +265,13 @@ public function testProviders(): void 'access' => '@=default_access', 'public' => '@=default_public', ], + 'planet_isPlanetDestroyed' => [ + 'type' => 'Boolean!', + 'args' => ['planetId' => ['type' => 'Int!']], + 'resolve' => "@=call(service('Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository').isPlanetDestroyed, arguments({planetId: \"Int!\"}, args))", + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); @@ -265,6 +284,13 @@ public function testProviders(): void 'access' => '@=default_access', 'public' => '@=override_public', ], + 'planet_destroyPlanet' => [ + 'type' => 'Boolean!', + 'args' => ['planetId' => ['type' => 'Int!']], + 'resolve' => "@=call(service('Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository').destroyPlanet, arguments({planetId: \"Int!\"}, args))", + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); } @@ -279,6 +305,13 @@ public function testProvidersMultischema(): void 'access' => '@=default_access', 'public' => '@=default_public', ], + 'planet_isPlanetDestroyed' => [ + 'type' => 'Boolean!', + 'args' => ['planetId' => ['type' => 'Int!']], + 'resolve' => "@=call(service('Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository').isPlanetDestroyed, arguments({planetId: \"Int!\"}, args))", + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); @@ -291,6 +324,13 @@ public function testProvidersMultischema(): void 'access' => '@=default_access', 'public' => '@=override_public', ], + 'planet_destroyPlanet' => [ + 'type' => 'Boolean!', + 'args' => ['planetId' => ['type' => 'Int!']], + 'resolve' => "@=call(service('Overblog\\\\GraphQLBundle\\\\Tests\\\\Config\\\\Parser\\\\fixtures\\\\annotations\\\\Repository\\\\PlanetRepository').destroyPlanet, arguments({planetId: \"Int!\"}, args))", + 'access' => '@=default_access', + 'public' => '@=default_public', + ], ], ]); } diff --git a/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php b/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php index b7365cdc2..562dce356 100644 --- a/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php +++ b/tests/Config/Parser/fixtures/annotations/Repository/PlanetRepository.php @@ -62,4 +62,28 @@ public function createPlanetSchema2(array $planetInput): array { return []; } + + /** + * @GQL\Mutation(targetType={"RootMutation", "RootMutation2"}) + */ + public function destroyPlanet(int $planetId): bool + { + return true; + } + + /** + * @GQL\Query(targetType={"RootQuery", "RootQuery2"}) + */ + public function isPlanetDestroyed(int $planetId): bool + { + return true; + } + + /** + * @GQL\Query(targetType={"Droid", "Mandalorian"}, name="armorResistance") + */ + public function getArmorResistance(): int + { + return 10; + } }