diff --git a/src/Generator/TypeBuilder.php b/src/Generator/TypeBuilder.php index 7a6aa3511..d4ccf8fa7 100644 --- a/src/Generator/TypeBuilder.php +++ b/src/Generator/TypeBuilder.php @@ -144,14 +144,15 @@ public function build(array $config, string $type): PhpFile /** * Converts a native GraphQL type string into the `webonyx/graphql-php` - * type literal. + * type literal. References to user-defined types are converted into + * TypeResovler method call and wrapped into a closure. * * Render examples: * * - "String" -> Type::string() * - "String!" -> Type::nonNull(Type::string()) * - "[String!] -> Type::listOf(Type::nonNull(Type::string())) - * - "[Post]" -> Type::listOf($globalVariables->get('typeResolver')->resolve('Post')) + * - "[Post]" -> fn() => Type::listOf($globalVariables->get('typeResolver')->resolve('Post')) * * @return GeneratorInterface|string */ @@ -159,7 +160,16 @@ protected function buildType(string $typeDefinition) { $typeNode = Parser::parseType($typeDefinition); - return $this->wrapTypeRecursive($typeNode); + $isReference = false; + $type = $this->wrapTypeRecursive($typeNode, $isReference); + + if ($isReference) { + // References to other types should be wrapped in a closure + // for performance reasons + return ArrowFunction::new($type); + } + + return $type; } /** @@ -169,20 +179,20 @@ protected function buildType(string $typeDefinition) * * @return DependencyAwareGenerator|string */ - protected function wrapTypeRecursive($typeNode) + protected function wrapTypeRecursive($typeNode, bool &$isReference) { switch ($typeNode->kind) { case NodeKind::NON_NULL_TYPE: - $innerType = $this->wrapTypeRecursive($typeNode->type); + $innerType = $this->wrapTypeRecursive($typeNode->type, $isReference); $type = Literal::new("Type::nonNull($innerType)"); $this->file->addUse(Type::class); break; case NodeKind::LIST_TYPE: - $innerType = $this->wrapTypeRecursive($typeNode->type); + $innerType = $this->wrapTypeRecursive($typeNode->type, $isReference); $type = Literal::new("Type::listOf($innerType)"); $this->file->addUse(Type::class); break; - default: + default: // NodeKind::NAMED_TYPE if (in_array($typeNode->name->value, static::BUILT_IN_TYPES)) { $name = strtolower($typeNode->name->value); $type = Literal::new("Type::$name()"); @@ -190,6 +200,7 @@ protected function wrapTypeRecursive($typeNode) } else { $name = $typeNode->name->value; $type = "$this->globalVars->get('typeResolver')->resolve('$name')"; + $isReference = true; } break; } diff --git a/src/Resolver/TypeResolver.php b/src/Resolver/TypeResolver.php index 7bfec2c04..8aa4ee784 100644 --- a/src/Resolver/TypeResolver.php +++ b/src/Resolver/TypeResolver.php @@ -4,15 +4,11 @@ namespace Overblog\GraphQLBundle\Resolver; -use GraphQL\Type\Definition\NullableType; use GraphQL\Type\Definition\Type; use Overblog\GraphQLBundle\Event\Events; use Overblog\GraphQLBundle\Event\TypeLoadedEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use function json_encode; use function sprintf; -use function strlen; -use function substr; class TypeResolver extends AbstractResolver { @@ -53,22 +49,13 @@ public function resolve($alias): ?Type } if (!isset($this->cache[$alias])) { - $type = $this->string2Type($alias); + $type = $this->baseType($alias); $this->cache[$alias] = $type; } return $this->cache[$alias]; } - private function string2Type(string $alias): Type - { - if (null !== ($type = $this->wrapTypeIfNeeded($alias))) { - return $type; - } - - return $this->baseType($alias); - } - private function baseType(string $alias): Type { $type = $this->getSolution($alias); @@ -81,39 +68,6 @@ private function baseType(string $alias): Type return $type; } - private function wrapTypeIfNeeded(string $alias): ?Type - { - // Non-Null - if ('!' === $alias[strlen($alias) - 1]) { - /** @var NullableType $type */ - $type = $this->string2Type(substr($alias, 0, -1)); - - return Type::nonNull($type); - } - // List - if ($this->hasNeedListOfWrapper($alias)) { - return Type::listOf($this->string2Type(substr($alias, 1, -1))); - } - - return null; - } - - private function hasNeedListOfWrapper(string $alias): bool - { - if ('[' === $alias[0]) { - $got = $alias[strlen($alias) - 1]; - if (']' !== $got) { - throw new UnresolvableException( - sprintf('Malformed ListOf wrapper type "%s" expected "]" but got "%s".', $alias, json_encode($got)) - ); - } - - return true; - } - - return false; - } - protected function supportedSolutionClass(): ?string { return Type::class; diff --git a/tests/Resolver/TypeResolverTest.php b/tests/Resolver/TypeResolverTest.php index e60c81665..396c55b9d 100644 --- a/tests/Resolver/TypeResolverTest.php +++ b/tests/Resolver/TypeResolverTest.php @@ -4,11 +4,8 @@ namespace Overblog\GraphQLBundle\Tests\Resolver; -use GraphQL\Type\Definition\ListOfType; -use GraphQL\Type\Definition\NonNull; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; -use GraphQL\Type\Definition\WrappingType; use Overblog\GraphQLBundle\Resolver\TypeResolver; use Overblog\GraphQLBundle\Resolver\UnresolvableException; use Overblog\GraphQLBundle\Resolver\UnsupportedResolverException; @@ -60,72 +57,6 @@ public function testResolveUnknownType(): void $this->resolver->resolve('Fake'); } - public function testWrongListOfWrappingType(): void - { - $this->expectException(UnresolvableException::class); - $this->expectExceptionMessage('Malformed ListOf wrapper type "[Tata" expected "]" but got ""a"".'); - $this->resolver->resolve('[Tata'); - } - - public function testResolveWithListOfWrapper(): void - { - /** @var WrappingType $type */ - $type = $this->resolver->resolve('[Tata]'); - - $this->assertInstanceOf(ListOfType::class, $type); - $this->assertSame('Tata', $type->getWrappedType()->name); - } - - public function testResolveWithNonNullWrapper(): void - { - /** @var WrappingType $type */ - $type = $this->resolver->resolve('Toto!'); - - $this->assertInstanceOf(NonNull::class, $type); - $this->assertSame('Toto', $type->getWrappedType()->name); - } - - public function testResolveWithNonNullListOfWrapper(): void - { - /** @var NonNull $type */ - $type = $this->resolver->resolve('[Toto]!'); - - $this->assertInstanceOf(NonNull::class, $type); - $this->assertInstanceOf(ListOfType::class, $type->getWrappedType()); - $this->assertSame('Toto', $type->getWrappedType()->getWrappedType()->name); - } - - public function testResolveWitListOfNonNullWrapper(): void - { - /** @var ListOfType $type */ - $type = $this->resolver->resolve('[Toto!]'); - - $this->assertInstanceOf(ListOfType::class, $type); - $this->assertInstanceOf(NonNull::class, $type->getWrappedType()); - $this->assertSame('Toto', $type->getWrappedType()->getWrappedType()->name); - } - - public function testResolveWitNonNullListOfNonNullWrapper(): void - { - /** @var NonNull $type */ - $type = $this->resolver->resolve('[Toto!]!'); - - $this->assertInstanceOf(NonNull::class, $type); - $this->assertInstanceOf(ListOfType::class, $type->getWrappedType()); - $this->assertInstanceOf(NonNull::class, $type->getWrappedType()->getWrappedType()); - $this->assertSame('Toto', $type->getWrappedType()->getWrappedType()->getWrappedType()->name); - } - - public function testResolveWitListOfListOfWrapper(): void - { - /** @var ListOfType $type */ - $type = $this->resolver->resolve('[[Toto]]'); - - $this->assertInstanceOf(ListOfType::class, $type); - $this->assertInstanceOf(ListOfType::class, $type->getWrappedType()); - $this->assertSame('Toto', $type->getWrappedType()->getWrappedType()->name); - } - public function testAliases(): void { $this->assertSame(