diff --git a/src/FieldsBuilder.php b/src/FieldsBuilder.php index d6438fd..c65dda9 100644 --- a/src/FieldsBuilder.php +++ b/src/FieldsBuilder.php @@ -82,18 +82,15 @@ class FieldsBuilder * @var TypeResolver */ private $typeResolver; - /** - * @param AnnotationReader $annotationReader - * @param RecursiveTypeMapperInterface $typeMapper - * @param HydratorInterface $hydrator - * @param AuthenticationServiceInterface $authenticationService - * @param AuthorizationServiceInterface $authorizationService + * @var NamingStrategyInterface */ + private $namingStrategy; + public function __construct(AnnotationReader $annotationReader, RecursiveTypeMapperInterface $typeMapper, HydratorInterface $hydrator, AuthenticationServiceInterface $authenticationService, AuthorizationServiceInterface $authorizationService, TypeResolver $typeResolver, - CachedDocBlockFactory $cachedDocBlockFactory) + CachedDocBlockFactory $cachedDocBlockFactory, NamingStrategyInterface $namingStrategy) { $this->annotationReader = $annotationReader; $this->typeMapper = $typeMapper; @@ -102,6 +99,7 @@ public function __construct(AnnotationReader $annotationReader, RecursiveTypeMap $this->authorizationService = $authorizationService; $this->typeResolver = $typeResolver; $this->cachedDocBlockFactory = $cachedDocBlockFactory; + $this->namingStrategy = $namingStrategy; } // TODO: Add RecursiveTypeMapper in the list of parameters for getQueries and REMOVE the ControllerQueryProviderFactory. @@ -217,7 +215,7 @@ private function getFieldsByAnnotations($controller, string $annotationName, boo $docBlockComment = $docBlockObj->getSummary()."\n".$docBlockObj->getDescription()->render(); $methodName = $refMethod->getName(); - $name = $queryAnnotation->getName() ?: $methodName; + $name = $queryAnnotation->getName() ?: $this->namingStrategy->getFieldNameFromMethodName($methodName); $parameters = $refMethod->getParameters(); if ($injectSource === true) { diff --git a/src/FieldsBuilderFactory.php b/src/FieldsBuilderFactory.php index 34ee5f0..eebf962 100644 --- a/src/FieldsBuilderFactory.php +++ b/src/FieldsBuilderFactory.php @@ -37,11 +37,15 @@ class FieldsBuilderFactory * @var TypeResolver */ private $typeResolver; + /** + * @var NamingStrategyInterface + */ + private $namingStrategy; public function __construct(AnnotationReader $annotationReader, HydratorInterface $hydrator, AuthenticationServiceInterface $authenticationService, AuthorizationServiceInterface $authorizationService, TypeResolver $typeResolver, - CachedDocBlockFactory $cachedDocBlockFactory) + CachedDocBlockFactory $cachedDocBlockFactory, NamingStrategyInterface $namingStrategy) { $this->annotationReader = $annotationReader; $this->hydrator = $hydrator; @@ -49,6 +53,7 @@ public function __construct(AnnotationReader $annotationReader, $this->authorizationService = $authorizationService; $this->typeResolver = $typeResolver; $this->cachedDocBlockFactory = $cachedDocBlockFactory; + $this->namingStrategy = $namingStrategy; } /** @@ -64,7 +69,8 @@ public function buildFieldsBuilder(RecursiveTypeMapperInterface $typeMapper): Fi $this->authenticationService, $this->authorizationService, $this->typeResolver, - $this->cachedDocBlockFactory + $this->cachedDocBlockFactory, + $this->namingStrategy ); } } diff --git a/src/NamingStrategy.php b/src/NamingStrategy.php index 3d9a114..ee4fa48 100644 --- a/src/NamingStrategy.php +++ b/src/NamingStrategy.php @@ -4,6 +4,10 @@ namespace TheCodingMachine\GraphQL\Controllers; +use function lcfirst; +use function strlen; +use function strpos; +use function substr; use TheCodingMachine\GraphQL\Controllers\Annotations\Factory; use TheCodingMachine\GraphQL\Controllers\Annotations\Type; @@ -49,4 +53,19 @@ public function getInputTypeName(string $className, Factory $factory): string } return $className.'Input'; } + + /** + * Returns the name of a GraphQL field from the name of the annotated method. + */ + public function getFieldNameFromMethodName(string $methodName): string + { + // Let's remove any "get" or "is". + if (strpos($methodName, 'get') === 0 && strlen($methodName) > 3) { + return lcfirst(substr($methodName, 3)); + } + if (strpos($methodName, 'is') === 0 && strlen($methodName) > 2) { + return lcfirst(substr($methodName, 2)); + } + return $methodName; + } } diff --git a/src/NamingStrategyInterface.php b/src/NamingStrategyInterface.php index f7a15c1..081110b 100644 --- a/src/NamingStrategyInterface.php +++ b/src/NamingStrategyInterface.php @@ -19,4 +19,9 @@ public function getInterfaceNameFromConcreteName(string $concreteType): string; public function getOutputTypeName(string $typeClassName, Type $type): string; public function getInputTypeName(string $className, Factory $factory): string; + + /** + * Returns the name of a GraphQL field from the name of the annotated method. + */ + public function getFieldNameFromMethodName(string $methodName): string; } diff --git a/tests/AbstractQueryProviderTest.php b/tests/AbstractQueryProviderTest.php index a35a83a..4caf873 100644 --- a/tests/AbstractQueryProviderTest.php +++ b/tests/AbstractQueryProviderTest.php @@ -259,7 +259,8 @@ protected function buildFieldsBuilder(): FieldsBuilder new VoidAuthenticationService(), new VoidAuthorizationService(), $this->getTypeResolver(), - new CachedDocBlockFactory(new ArrayCache()) + new CachedDocBlockFactory(new ArrayCache()), + new NamingStrategy() ); } @@ -304,7 +305,8 @@ protected function getControllerQueryProviderFactory(): FieldsBuilderFactory new VoidAuthenticationService(), new VoidAuthorizationService(), $this->getTypeResolver(), - new CachedDocBlockFactory(new ArrayCache())); + new CachedDocBlockFactory(new ArrayCache()), + new NamingStrategy()); } return $this->controllerQueryProviderFactory; } diff --git a/tests/FieldsBuilderTest.php b/tests/FieldsBuilderTest.php index c109735..3d55299 100644 --- a/tests/FieldsBuilderTest.php +++ b/tests/FieldsBuilderTest.php @@ -195,7 +195,8 @@ public function isLogged(): bool }, new VoidAuthorizationService(), $this->getTypeResolver(), - new CachedDocBlockFactory(new ArrayCache()) + new CachedDocBlockFactory(new ArrayCache()), + new NamingStrategy() ); $fields = $queryProvider->getFields(new TestType(), true); @@ -219,7 +220,8 @@ public function isAllowed(string $right): bool } }, $this->getTypeResolver(), - new CachedDocBlockFactory(new ArrayCache()) + new CachedDocBlockFactory(new ArrayCache()), + new NamingStrategy() ); $fields = $queryProvider->getFields(new TestType(), true); @@ -275,7 +277,8 @@ public function testFromSourceFieldsInterface() new VoidAuthenticationService(), new VoidAuthorizationService(), $this->getTypeResolver(), - new CachedDocBlockFactory(new ArrayCache()) + new CachedDocBlockFactory(new ArrayCache()), + new NamingStrategy() ); $fields = $queryProvider->getFields(new TestTypeWithSourceFieldInterface(), true); $this->assertCount(1, $fields); diff --git a/tests/Fixtures/Integration/Models/Product.php b/tests/Fixtures/Integration/Models/Product.php index aebb2c9..4feb215 100644 --- a/tests/Fixtures/Integration/Models/Product.php +++ b/tests/Fixtures/Integration/Models/Product.php @@ -44,7 +44,7 @@ public function getName(): string } /** - * @Field(name="price") + * @Field() * @return float */ public function getPrice(): float diff --git a/tests/Integration/EndToEndTest.php b/tests/Integration/EndToEndTest.php index d7bcf0c..ab1980a 100644 --- a/tests/Integration/EndToEndTest.php +++ b/tests/Integration/EndToEndTest.php @@ -71,7 +71,8 @@ public function setUp() $container->get(AuthenticationServiceInterface::class), $container->get(AuthorizationServiceInterface::class), $container->get(TypeResolver::class), - $container->get(CachedDocBlockFactory::class) + $container->get(CachedDocBlockFactory::class), + $container->get(NamingStrategyInterface::class) ); }, TypeResolver::class => function(ContainerInterface $container) { @@ -179,7 +180,7 @@ public function testEndToEnd() $queryString = ' query { - getContacts { + contacts { name uppercaseName ... on User { @@ -195,7 +196,7 @@ public function testEndToEnd() ); $this->assertSame([ - 'getContacts' => [ + 'contacts' => [ [ 'name' => 'Joe', 'uppercaseName' => 'JOE' @@ -216,7 +217,7 @@ public function testEndToEnd() ); $this->assertSame([ - 'getContacts' => [ + 'contacts' => [ [ 'name' => 'Joe', 'uppercaseName' => 'JOE' @@ -289,7 +290,7 @@ public function testEndToEndPorpaginas() $queryString = ' query { - getContactsIterator { + contactsIterator { items(limit: 1, offset: 1) { name uppercaseName @@ -308,7 +309,7 @@ public function testEndToEndPorpaginas() ); $this->assertSame([ - 'getContactsIterator' => [ + 'contactsIterator' => [ 'items' => [ [ 'name' => 'Bill', @@ -327,7 +328,7 @@ public function testEndToEndPorpaginas() ); $this->assertSame([ - 'getContactsIterator' => [ + 'contactsIterator' => [ 'items' => [ [ 'name' => 'Bill', @@ -342,7 +343,7 @@ public function testEndToEndPorpaginas() // Let's run a query with no limit but an offset $invalidQueryString = ' query { - getContactsIterator { + contactsIterator { items(offset: 1) { name ... on User { @@ -365,7 +366,7 @@ public function testEndToEndPorpaginas() // Let's run a query with no limit offset $invalidQueryString = ' query { - getContactsIterator { + contactsIterator { items { name ... on User { @@ -383,7 +384,7 @@ public function testEndToEndPorpaginas() ); $this->assertSame([ - 'getContactsIterator' => [ + 'contactsIterator' => [ 'items' => [ [ 'name' => 'Joe', @@ -410,7 +411,7 @@ public function testEndToEnd2Iterators() $queryString = ' query { - getContactsIterator { + contactsIterator { items(limit: 1, offset: 1) { name uppercaseName @@ -421,7 +422,7 @@ public function testEndToEnd2Iterators() count } - getProducts { + products { items { name price @@ -437,7 +438,7 @@ public function testEndToEnd2Iterators() ); $this->assertSame([ - 'getContactsIterator' => [ + 'contactsIterator' => [ 'items' => [ [ 'name' => 'Bill', @@ -447,7 +448,7 @@ public function testEndToEnd2Iterators() ], 'count' => 2 ], - 'getProducts' => [ + 'products' => [ 'items' => [ [ 'name' => 'Foo', diff --git a/tests/NamingStrategyTest.php b/tests/NamingStrategyTest.php index 0355035..a2d1a62 100644 --- a/tests/NamingStrategyTest.php +++ b/tests/NamingStrategyTest.php @@ -18,4 +18,15 @@ public function testGetInputTypeName(): void $factory = new Factory(['name'=>'MyInputType']); $this->assertSame('MyInputType', $namingStrategy->getInputTypeName('Bar\\FooClass', $factory)); } + + public function testGetFieldNameFromMethodName(): void + { + $namingStrategy = new NamingStrategy(); + + $this->assertSame('name', $namingStrategy->getFieldNameFromMethodName('getName')); + $this->assertSame('get', $namingStrategy->getFieldNameFromMethodName('get')); + $this->assertSame('name', $namingStrategy->getFieldNameFromMethodName('isName')); + $this->assertSame('is', $namingStrategy->getFieldNameFromMethodName('is')); + $this->assertSame('foo', $namingStrategy->getFieldNameFromMethodName('foo')); + } }