diff --git a/src/Mappers/Root/CompoundTypeMapper.php b/src/Mappers/Root/CompoundTypeMapper.php index ed9f995873..70323ae80a 100644 --- a/src/Mappers/Root/CompoundTypeMapper.php +++ b/src/Mappers/Root/CompoundTypeMapper.php @@ -23,6 +23,7 @@ use RuntimeException; use TheCodingMachine\GraphQLite\Mappers\CannotMapTypeException; use TheCodingMachine\GraphQLite\Mappers\RecursiveTypeMapperInterface; +use TheCodingMachine\GraphQLite\NamingStrategyInterface; use TheCodingMachine\GraphQLite\TypeRegistry; use TheCodingMachine\GraphQLite\Types\UnionType; @@ -38,8 +39,13 @@ */ class CompoundTypeMapper implements RootTypeMapperInterface { - public function __construct(private RootTypeMapperInterface $next, private RootTypeMapperInterface $topRootTypeMapper, private TypeRegistry $typeRegistry, private RecursiveTypeMapperInterface $recursiveTypeMapper) - { + public function __construct( + private RootTypeMapperInterface $next, + private RootTypeMapperInterface $topRootTypeMapper, + private NamingStrategyInterface $namingStrategy, + private TypeRegistry $typeRegistry, + private RecursiveTypeMapperInterface $recursiveTypeMapper, + ) { } public function toGraphQLOutputType(Type $type, OutputType|null $subType, ReflectionMethod|ReflectionProperty $reflector, DocBlock $docBlockObj): OutputType @@ -144,7 +150,7 @@ private function getTypeFromUnion(array $unionTypes): GraphQLType throw CannotMapTypeException::createForBadTypeInUnion($unionTypes); } - $graphQlType = new UnionType($nonNullableUnionTypes, $this->recursiveTypeMapper); + $graphQlType = new UnionType($nonNullableUnionTypes, $this->recursiveTypeMapper, $this->namingStrategy); $graphQlType = $this->typeRegistry->getOrRegisterType($graphQlType); assert($graphQlType instanceof UnionType); } diff --git a/src/NamingStrategy.php b/src/NamingStrategy.php index ecb82b21a0..bb308dbb73 100644 --- a/src/NamingStrategy.php +++ b/src/NamingStrategy.php @@ -8,6 +8,7 @@ use TheCodingMachine\GraphQLite\Annotations\Input; use TheCodingMachine\GraphQLite\Annotations\TypeInterface; +use function implode; use function lcfirst; use function str_ends_with; use function str_replace; @@ -109,4 +110,14 @@ public function getInputFieldNameFromMethodName(string $methodName): string return $methodName; } + + /** + * Returns the name of a GraphQL union type based on the included types. + * + * @param string[] $typeNames The list of GraphQL type names + */ + public function getUnionTypeName(array $typeNames): string + { + return 'Union' . implode('', $typeNames); + } } diff --git a/src/NamingStrategyInterface.php b/src/NamingStrategyInterface.php index aee6f61eb9..c1e29cf74d 100644 --- a/src/NamingStrategyInterface.php +++ b/src/NamingStrategyInterface.php @@ -44,4 +44,11 @@ public function getFieldNameFromMethodName(string $methodName): string; * Returns the name of a GraphQL input field from the name of the annotated method. */ public function getInputFieldNameFromMethodName(string $methodName): string; + + /** + * Returns the name of a GraphQL union type based on the included types. + * + * @param string[] $typeNames The list of GraphQL type names + */ + public function getUnionTypeName(array $typeNames): string; } diff --git a/src/SchemaFactory.php b/src/SchemaFactory.php index abaac63745..b7dac89888 100644 --- a/src/SchemaFactory.php +++ b/src/SchemaFactory.php @@ -407,7 +407,7 @@ public function createSchema(): Schema } } - $rootTypeMapper = new CompoundTypeMapper($rootTypeMapper, $topRootTypeMapper, $typeRegistry, $recursiveTypeMapper); + $rootTypeMapper = new CompoundTypeMapper($rootTypeMapper, $topRootTypeMapper, $namingStrategy, $typeRegistry, $recursiveTypeMapper); $rootTypeMapper = new IteratorTypeMapper($rootTypeMapper, $topRootTypeMapper); $topRootTypeMapper->setNext($rootTypeMapper); diff --git a/src/Types/UnionType.php b/src/Types/UnionType.php index b923ea2211..30aaf0ef8a 100644 --- a/src/Types/UnionType.php +++ b/src/Types/UnionType.php @@ -7,22 +7,31 @@ use GraphQL\Type\Definition\ObjectType; use InvalidArgumentException; use TheCodingMachine\GraphQLite\Mappers\RecursiveTypeMapperInterface; +use TheCodingMachine\GraphQLite\NamingStrategyInterface; +use function array_map; use function gettype; use function is_object; class UnionType extends \GraphQL\Type\Definition\UnionType { /** @param array $types */ - public function __construct(array $types, RecursiveTypeMapperInterface $typeMapper) + public function __construct( + array $types, + RecursiveTypeMapperInterface $typeMapper, + NamingStrategyInterface $namingStrategy, + ) { - $name = 'Union'; + // Make sure all types are object types foreach ($types as $type) { - $name .= $type->name; if (! $type instanceof ObjectType) { throw InvalidTypesInUnionException::notObjectType(); } } + + $typeNames = array_map(static fn (ObjectType $type) => $type->name, $types); + $name = $namingStrategy->getUnionTypeName($typeNames); + parent::__construct([ 'name' => $name, 'types' => $types, diff --git a/tests/AbstractQueryProviderTest.php b/tests/AbstractQueryProviderTest.php index 2970f7cf6a..2c1ba84bc8 100644 --- a/tests/AbstractQueryProviderTest.php +++ b/tests/AbstractQueryProviderTest.php @@ -351,6 +351,7 @@ protected function buildRootTypeMapper(): RootTypeMapperInterface $rootTypeMapper = new CompoundTypeMapper( $rootTypeMapper, $topRootTypeMapper, + new NamingStrategy(), $this->getTypeRegistry(), $this->getTypeMapper() ); diff --git a/tests/Integration/EndToEndTest.php b/tests/Integration/EndToEndTest.php index c5756cc58b..b1f75ee64f 100644 --- a/tests/Integration/EndToEndTest.php +++ b/tests/Integration/EndToEndTest.php @@ -296,7 +296,7 @@ public function createContainer(array $overloadedServices = []): ContainerInterf if (interface_exists(UnitEnum::class)) { $rootTypeMapper = new EnumTypeMapper($rootTypeMapper, $container->get(AnnotationReader::class), new ArrayAdapter(), [ $container->get(NamespaceFactory::class)->createNamespace('TheCodingMachine\\GraphQLite\\Fixtures81\\Integration\\Models') ]); } - $rootTypeMapper = new CompoundTypeMapper($rootTypeMapper, $container->get(RootTypeMapperInterface::class), $container->get(TypeRegistry::class), $container->get(RecursiveTypeMapperInterface::class)); + $rootTypeMapper = new CompoundTypeMapper($rootTypeMapper, $container->get(RootTypeMapperInterface::class), $container->get(NamingStrategyInterface::class), $container->get(TypeRegistry::class), $container->get(RecursiveTypeMapperInterface::class)); $rootTypeMapper = new IteratorTypeMapper($rootTypeMapper, $container->get(RootTypeMapperInterface::class)); return $rootTypeMapper; }, diff --git a/tests/Mappers/Root/CompoundTypeMapperTest.php b/tests/Mappers/Root/CompoundTypeMapperTest.php index 0f5dbd0360..ae36608d9f 100644 --- a/tests/Mappers/Root/CompoundTypeMapperTest.php +++ b/tests/Mappers/Root/CompoundTypeMapperTest.php @@ -11,6 +11,7 @@ use RuntimeException; use TheCodingMachine\GraphQLite\AbstractQueryProviderTest; use TheCodingMachine\GraphQLite\Mappers\CannotMapTypeException; +use TheCodingMachine\GraphQLite\NamingStrategy; class CompoundTypeMapperTest extends AbstractQueryProviderTest { @@ -19,6 +20,7 @@ public function testException1() $compoundTypeMapper = new CompoundTypeMapper( new FinalRootTypeMapper($this->getTypeMapper()), new FinalRootTypeMapper($this->getTypeMapper()), + new NamingStrategy(), $this->getTypeRegistry(), $this->getTypeMapper() ); @@ -32,6 +34,7 @@ public function testException2() $compoundTypeMapper = new CompoundTypeMapper( new FinalRootTypeMapper($this->getTypeMapper()), new FinalRootTypeMapper($this->getTypeMapper()), + new NamingStrategy(), $this->getTypeRegistry(), $this->getTypeMapper() ); diff --git a/tests/NamingStrategyTest.php b/tests/NamingStrategyTest.php index 5943207a89..157bb5f9d1 100644 --- a/tests/NamingStrategyTest.php +++ b/tests/NamingStrategyTest.php @@ -9,7 +9,6 @@ class NamingStrategyTest extends TestCase { - public function testGetInputTypeName(): void { $namingStrategy = new NamingStrategy(); @@ -43,4 +42,13 @@ public function testGetFieldNameFromTypeAnnotation(): void $name = $namingStrategy->getOutputTypeName(TestObject::class, $type); $this->assertSame('foo', $name); } + + public function testGetUnionTypeName(): void + { + $namingStrategy = new NamingStrategy(); + + $typeNames = ['Some', 'Arbitrary', 'Type', 'Names']; + $name = $namingStrategy->getUnionTypeName($typeNames); + $this->assertSame('UnionSomeArbitraryTypeNames', $name); + } } diff --git a/tests/Types/UnionTypeTest.php b/tests/Types/UnionTypeTest.php index 580b45702a..122c326e57 100644 --- a/tests/Types/UnionTypeTest.php +++ b/tests/Types/UnionTypeTest.php @@ -8,12 +8,13 @@ use TheCodingMachine\GraphQLite\AbstractQueryProviderTest; use TheCodingMachine\GraphQLite\Fixtures\TestObject; use TheCodingMachine\GraphQLite\Fixtures\TestObject2; +use TheCodingMachine\GraphQLite\NamingStrategy; class UnionTypeTest extends AbstractQueryProviderTest { public function testConstructor(): void { - $unionType = new UnionType([$this->getTestObjectType(), $this->getTestObjectType2()], $this->getTypeMapper()); + $unionType = new UnionType([$this->getTestObjectType(), $this->getTestObjectType2()], $this->getTypeMapper(), new NamingStrategy()); $resolveInfo = $this->getMockBuilder(ResolveInfo::class)->disableOriginalConstructor()->getMock(); $type = $unionType->resolveType(new TestObject('foo'), null, $resolveInfo); $this->assertSame($this->getTestObjectType(), $type); @@ -23,7 +24,7 @@ public function testConstructor(): void public function testException(): void { - $unionType = new UnionType([$this->getTestObjectType(), $this->getTestObjectType2()], $this->getTypeMapper()); + $unionType = new UnionType([$this->getTestObjectType(), $this->getTestObjectType2()], $this->getTypeMapper(), new NamingStrategy()); $this->expectException(\InvalidArgumentException::class); $resolveInfo = $this->getMockBuilder(ResolveInfo::class)->disableOriginalConstructor()->getMock(); $unionType->resolveType('foo', null, $resolveInfo); @@ -32,6 +33,6 @@ public function testException(): void public function testException2(): void { $this->expectException(\InvalidArgumentException::class); - new UnionType([new StringType()], $this->getTypeMapper()); + new UnionType([new StringType()], $this->getTypeMapper(), new NamingStrategy()); } }