Skip to content

Commit 4a10c31

Browse files
committed
Adding support for self-types, @ExtendType and Porpaginas interfaces
1 parent 9a12c46 commit 4a10c31

File tree

11 files changed

+255
-51
lines changed

11 files changed

+255
-51
lines changed

DependencyInjection/Configuration.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,16 @@ public function getConfigTreeBuilder()
1919
//->scalarNode('types_namespace')->defaultValue('App\\Types')->end()
2020
->arrayNode('namespace')->isRequired()
2121
->children()
22-
->scalarNode('controllers')->isRequired()->cannotBeEmpty()->end()
23-
->scalarNode('types')->isRequired()->cannotBeEmpty()->end()
22+
->arrayNode('controllers')
23+
->requiresAtLeastOneElement()
24+
->beforeNormalization()->castToArray()->end()
25+
->scalarPrototype()->cannotBeEmpty()->end()
26+
->end()
27+
->arrayNode('types')
28+
->requiresAtLeastOneElement()
29+
->beforeNormalization()->castToArray()->end()
30+
->scalarPrototype()->cannotBeEmpty()->end()
31+
->end()
2432
->end()
2533
->end()
2634
->arrayNode('debug')

DependencyInjection/GraphqlControllersCompilerPass.php

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace TheCodingMachine\Graphql\Controllers\Bundle\DependencyInjection;
55

66
use function class_exists;
7-
use function dirname;
87
use Doctrine\Common\Annotations\AnnotationException;
98
use Doctrine\Common\Annotations\AnnotationReader as DoctrineAnnotationReader;
109
use Doctrine\Common\Annotations\AnnotationRegistry;
@@ -15,6 +14,7 @@
1514
use GraphQL\Type\Definition\ObjectType;
1615
use Psr\Container\ContainerInterface;
1716
use ReflectionClass;
17+
use function str_replace;
1818
use function strpos;
1919
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
2020
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -28,6 +28,7 @@
2828
use TheCodingMachine\GraphQL\Controllers\FieldsBuilderFactory;
2929
use TheCodingMachine\GraphQL\Controllers\InputTypeGenerator;
3030
use TheCodingMachine\GraphQL\Controllers\InputTypeUtils;
31+
use TheCodingMachine\GraphQL\Controllers\Mappers\GlobTypeMapper;
3132
use TheCodingMachine\GraphQL\Controllers\Mappers\RecursiveTypeMapperInterface;
3233
use TheCodingMachine\GraphQL\Controllers\Mappers\StaticTypeMapper;
3334
use TheCodingMachine\GraphQL\Controllers\NamingStrategy;
@@ -66,21 +67,39 @@ public function process(ContainerBuilder $container)
6667

6768
$namingStrategy = new NamingStrategy();
6869
$reader = $this->getAnnotationReader();
69-
$inputTypeUtils = new InputTypeUtils($reader, $namingStrategy);
70+
//$inputTypeUtils = new InputTypeUtils($reader, $namingStrategy);
7071

7172
// Let's scan the whole container and tag the services that belong to the namespace we want to inspect.
72-
$controllersNamespace = $container->getParameter('graphql_controllers.namespace.controllers');
73-
$typesNamespace = $container->getParameter('graphql_controllers.namespace.types');
73+
$controllersNamespaces = $container->getParameter('graphql_controllers.namespace.controllers');
74+
$typesNamespaces = $container->getParameter('graphql_controllers.namespace.types');
7475

7576
foreach ($container->getDefinitions() as $id => $definition) {
7677
if ($definition->isAbstract() || $definition->getClass() === null) {
7778
continue;
7879
}
79-
if (strpos($definition->getClass(), $controllersNamespace) === 0) {
80-
$definition->addTag('graphql.annotated.controller');
80+
$class = $definition->getClass();
81+
foreach ($controllersNamespaces as $controllersNamespace) {
82+
if (strpos($class, $controllersNamespace) === 0) {
83+
$definition->addTag('graphql.annotated.controller');
84+
}
8185
}
82-
if (strpos($definition->getClass(), $typesNamespace) === 0) {
83-
$definition->addTag('graphql.annotated.type');
86+
87+
foreach ($typesNamespaces as $typesNamespace) {
88+
if (strpos($class, $typesNamespace) === 0) {
89+
//$definition->addTag('graphql.annotated.type');
90+
// Set the types public
91+
$reflectionClass = new ReflectionClass($class);
92+
if ($this->getAnnotationReader()->getTypeAnnotation($reflectionClass) !== null || $this->getAnnotationReader()->getExtendTypeAnnotation($reflectionClass) !== null) {
93+
$definition->setPublic(true);
94+
} else {
95+
foreach ($reflectionClass->getMethods() as $method) {
96+
$factory = $reader->getFactoryAnnotation($method);
97+
if ($factory !== null) {
98+
$definition->setPublic(true);
99+
}
100+
}
101+
}
102+
}
84103
}
85104
}
86105

@@ -120,8 +139,9 @@ public function process(ContainerBuilder $container)
120139
$container->setDefinition($controllerIdentifier, $queryProvider);
121140
}
122141
}
123-
142+
/*
124143
foreach ($container->findTaggedServiceIds('graphql.annotated.type') as $id => $tag) {
144+
$used = false;
125145
$definition = $container->findDefinition($id);
126146
$class = $definition->getClass();
127147
if ($class === null) {
@@ -148,6 +168,7 @@ public function process(ContainerBuilder $container)
148168
$inputTypes[$inputClassName] = $objectTypeIdentifier;
149169
$typesByName[$inputName] = $objectTypeIdentifier;
150170
171+
$used = true;
151172
}
152173
}
153174
@@ -158,27 +179,38 @@ public function process(ContainerBuilder $container)
158179
$objectType = new Definition(ObjectType::class);
159180
$objectType->setPrivate(false);
160181
$objectType->setFactory([self::class, 'createObjectType']);
161-
$objectType->addArgument(new Reference($id));
182+
$objectType->addArgument($id);
162183
$objectType->addArgument(new Reference(TypeGenerator::class));
163184
$objectType->addArgument(new Reference(RecursiveTypeMapperInterface::class));
164185
$container->setDefinition($objectTypeIdentifier, $objectType);
165186
166187
$types[$typeAnnotation->getClass()] = $objectTypeIdentifier;
167188
$typesByName[$namingStrategy->getOutputTypeName($class, $typeAnnotation)] = $objectTypeIdentifier;
168189
//$definition->addTag('graphql.annotated_type');
190+
191+
$used = true;
192+
}
193+
194+
// If the service is used for GraphQL, since it is referenced by service name in the factories, let's make it public
195+
if ($used) {
196+
$container->findDefinition($id)->setPublic(true);
169197
}
170198
}
171199
172200
$containerFetcherTypeMapper = $container->getDefinition(ContainerFetcherTypeMapper::class);
173201
$containerFetcherTypeMapper->replaceArgument(1, $types);
174202
$containerFetcherTypeMapper->replaceArgument(2, $inputTypes);
175203
$containerFetcherTypeMapper->replaceArgument(3, $typesByName);
176-
/*$containerFetcherTypeMapper = new Definition(ContainerFetcherTypeMapper::class);
177-
$containerFetcherTypeMapper->addArgument($container->getDefinition('service_container'));
178-
$containerFetcherTypeMapper->addArgument($types);
179-
$containerFetcherTypeMapper->addArgument([]);
180-
$containerFetcherTypeMapper->addTag('graphql.type_mapper');
181-
$container->setDefinition(ContainerFetcherTypeMapper::class, $containerFetcherTypeMapper);*/
204+
*/
205+
206+
foreach ($typesNamespaces as $typesNamespace) {
207+
$definition = new Definition(GlobTypeMapper::class);
208+
$definition->addArgument($typesNamespace);
209+
$definition->setAutowired(true);
210+
$definition->addTag('graphql.type_mapper');
211+
$container->setDefinition('globTypeMapper_'.str_replace('\\', '__', $typesNamespace), $definition);
212+
}
213+
182214

183215
// Register custom output types
184216
$taggedServices = $container->findTaggedServiceIds('graphql.output_type');

DependencyInjection/GraphqlControllersExtension.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ public function load(array $configs, ContainerBuilder $container)
3434
//$config = $this->processConfiguration($this->getConfiguration($config, $container), $config);
3535
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config/container'));
3636

37-
$namespaceController = rtrim($configs[0]['namespace']['controllers'], '\\') . '\\';
38-
$namespaceType = rtrim($configs[0]['namespace']['types'], '\\') . '\\';
37+
$namespaceController = array_map(function($namespace) { return rtrim($namespace, '\\') . '\\'; }, $configs[0]['namespace']['controllers']);
38+
$namespaceType = array_map(function($namespace) { return rtrim($namespace, '\\') . '\\'; }, $configs[0]['namespace']['types']);
3939

4040
$container->setParameter('graphql_controllers.namespace.controllers', $namespaceController);
4141
$container->setParameter('graphql_controllers.namespace.types', $namespaceType);

Mappers/ContainerFetcherTypeMapper.php

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
use GraphQL\Type\Definition\OutputType;
1111
use Psr\Container\ContainerInterface;
1212
use TheCodingMachine\GraphQL\Controllers\Mappers\CannotMapTypeException;
13+
use TheCodingMachine\GraphQL\Controllers\Mappers\CannotMapTypeExceptionInterface;
1314
use TheCodingMachine\GraphQL\Controllers\Mappers\RecursiveTypeMapperInterface;
1415
use TheCodingMachine\GraphQL\Controllers\Mappers\TypeMapperInterface;
16+
use TheCodingMachine\GraphQL\Controllers\Types\MutableObjectType;
1517

1618
/**
1719
* A type mapper that fetches types from the container that is directly injected in the type mapper.
@@ -67,17 +69,22 @@ public function canMapClassToType(string $className): bool
6769
}
6870

6971
/**
70-
* Maps a PHP fully qualified class name to a Graphql type.
72+
* Maps a PHP fully qualified class name to a GraphQL type.
7173
*
72-
* @param string $className
74+
* @param string $className The exact class name to look for (this function does not look into parent classes).
75+
* @param OutputType|null $subType An optional sub-type if the main class is an iterator that needs to be typed.
7376
* @param RecursiveTypeMapperInterface $recursiveTypeMapper
74-
* @return ObjectType
75-
* @throws CannotMapTypeException
77+
* @return MutableObjectType
78+
* @throws CannotMapTypeExceptionInterface
7679
*/
77-
public function mapClassToType(string $className, RecursiveTypeMapperInterface $recursiveTypeMapper): ObjectType
80+
public function mapClassToType(string $className, ?OutputType $subType, RecursiveTypeMapperInterface $recursiveTypeMapper): MutableObjectType
7881
{
79-
if (isset($this->types[$className])) {
80-
return $this->container->get($this->types[$className]);
82+
$key = $className;
83+
if ($subType !== null) {
84+
$key .= '____'.$subType->name;
85+
}
86+
if (isset($this->types[$key])) {
87+
return $this->container->get($this->types[$key]);
8188
}
8289
throw CannotMapTypeException::createForType($className);
8390
}
@@ -143,4 +150,56 @@ public function mapNameToType(string $typeName, RecursiveTypeMapperInterface $re
143150
}
144151
throw CannotMapTypeException::createForName($typeName);
145152
}
153+
154+
/**
155+
* Returns true if this type mapper can extend an existing type for the $className FQCN
156+
*
157+
* @param string $className
158+
* @param MutableObjectType $type
159+
* @param RecursiveTypeMapperInterface $recursiveTypeMapper
160+
* @return bool
161+
*/
162+
public function canExtendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool
163+
{
164+
return false;
165+
}
166+
167+
/**
168+
* Extends the existing GraphQL type that is mapped to $className.
169+
*
170+
* @param string $className
171+
* @param MutableObjectType $type
172+
* @param RecursiveTypeMapperInterface $recursiveTypeMapper
173+
* @throws CannotMapTypeExceptionInterface
174+
*/
175+
public function extendTypeForClass(string $className, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void
176+
{
177+
throw CannotMapTypeException::createForType($className);
178+
}
179+
180+
/**
181+
* Returns true if this type mapper can extend an existing type for the $typeName GraphQL type
182+
*
183+
* @param string $typeName
184+
* @param MutableObjectType $type
185+
* @param RecursiveTypeMapperInterface $recursiveTypeMapper
186+
* @return bool
187+
*/
188+
public function canExtendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): bool
189+
{
190+
return false;
191+
}
192+
193+
/**
194+
* Extends the existing GraphQL type that is mapped to the $typeName GraphQL type.
195+
*
196+
* @param string $typeName
197+
* @param MutableObjectType $type
198+
* @param RecursiveTypeMapperInterface $recursiveTypeMapper
199+
* @throws CannotMapTypeExceptionInterface
200+
*/
201+
public function extendTypeForName(string $typeName, MutableObjectType $type, RecursiveTypeMapperInterface $recursiveTypeMapper): void
202+
{
203+
throw CannotMapTypeException::createForName($typeName);
204+
}
146205
}

0 commit comments

Comments
 (0)