From 571b2d4af3687275e71e4fa84c007fdee3d303b5 Mon Sep 17 00:00:00 2001 From: Jeremiah VALERIE Date: Fri, 15 Sep 2017 19:10:19 +0200 Subject: [PATCH 1/2] Use Schema TypeLoader --- Definition/Builder/SchemaBuilder.php | 3 ++- Resolver/TypeResolver.php | 8 +++++++- Tests/Functional/App/config/global/config.yml | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Definition/Builder/SchemaBuilder.php b/Definition/Builder/SchemaBuilder.php index 858b9b1e3..f17f56269 100644 --- a/Definition/Builder/SchemaBuilder.php +++ b/Definition/Builder/SchemaBuilder.php @@ -47,7 +47,8 @@ public function create($queryAlias = null, $mutationAlias = null, $subscriptionA 'query' => $query, 'mutation' => $mutation, 'subscription' => $subscription, - 'types' => $this->typeResolver->getSolutions(), + 'typeLoader' => [$this->typeResolver, 'resolve'], + 'types' => [$this->typeResolver, 'getSolutions'], ]); if ($this->enableValidation) { $schema->assertValid(); diff --git a/Resolver/TypeResolver.php b/Resolver/TypeResolver.php index 74df98053..42c586c71 100644 --- a/Resolver/TypeResolver.php +++ b/Resolver/TypeResolver.php @@ -40,8 +40,14 @@ public function resolve($alias) $item = $this->cacheAdapter->getItem(md5($alias)); if (!$item->isHit()) { - $item->set($this->string2Type($alias)); + $type = $this->string2Type($alias); + $item->set($type); $this->cacheAdapter->save($item); + + // also add solution with real type name if needed for typeLoader when using autoMapping + if ($type && !isset($this->getSolutions()[$type->name])) { + $this->addSolution($type->name, $type); + } } return $item->get(); diff --git a/Tests/Functional/App/config/global/config.yml b/Tests/Functional/App/config/global/config.yml index d7538956f..7509022a6 100644 --- a/Tests/Functional/App/config/global/config.yml +++ b/Tests/Functional/App/config/global/config.yml @@ -12,6 +12,7 @@ services: overblog_graphql: definitions: + config_validation: false schema: query: Query mutation: ~ From 64abad3793e3f31370330862658ad2fc2c1265bc Mon Sep 17 00:00:00 2001 From: Jeremiah VALERIE Date: Sat, 16 Sep 2017 10:06:31 +0200 Subject: [PATCH 2/2] Add Resolver solution is now lazy loading --- .../Compiler/TaggedServiceMappingPass.php | 9 ++- Resolver/AbstractResolver.php | 70 ++++++++++++++++--- Resolver/ResolverInterface.php | 2 +- Resolver/TypeResolver.php | 15 ++-- Tests/Resolver/AbstractProxyResolverTest.php | 7 +- Tests/Resolver/AbstractResolverTest.php | 2 +- Tests/Resolver/TypeResolverTest.php | 14 +++- 7 files changed, 97 insertions(+), 22 deletions(-) diff --git a/DependencyInjection/Compiler/TaggedServiceMappingPass.php b/DependencyInjection/Compiler/TaggedServiceMappingPass.php index 71dc65e64..d6d9d9386 100644 --- a/DependencyInjection/Compiler/TaggedServiceMappingPass.php +++ b/DependencyInjection/Compiler/TaggedServiceMappingPass.php @@ -61,7 +61,9 @@ public function process(ContainerBuilder $container) $cleanOptions = $options; $solutionID = $options['id']; - $solutionDefinition = $container->findDefinition($options['id']); + $solutionDefinition = $container->findDefinition($solutionID); + // make solution service public to improve lazy loading + $solutionDefinition->setPublic(true); $methods = array_map( function ($methodCall) { @@ -80,7 +82,10 @@ function ($methodCall) { $solutionDefinition->addMethodCall('setContainer', [new Reference('service_container')]); } - $resolverDefinition->addMethodCall('addSolution', [$name, new Reference($solutionID), $cleanOptions]); + $resolverDefinition->addMethodCall( + 'addSolution', + [$name, [new Reference('service_container'), 'get'], [$solutionID], $cleanOptions] + ); } } diff --git a/Resolver/AbstractResolver.php b/Resolver/AbstractResolver.php index 926f3a33c..94d9e62aa 100644 --- a/Resolver/AbstractResolver.php +++ b/Resolver/AbstractResolver.php @@ -23,15 +23,20 @@ abstract class AbstractResolver implements ResolverInterface */ private $solutionOptions = []; - public function addSolution($name, $solution, $options = []) + /** + * @var array + */ + private $fullyLoadedSolutions = []; + + public function addSolution($name, callable $solutionFunc, array $solutionFuncArgs = [], array $options = []) { - if (!$this->supportsSolution($solution)) { - throw new UnsupportedResolverException( - sprintf('Resolver "%s" must be "%s" "%s" given.', $name, $this->supportedSolutionClass(), get_class($solution)) - ); - } + $this->fullyLoadedSolutions[$name] = false; + $this->solutions[$name] = function () use ($name, $solutionFunc, $solutionFuncArgs) { + $solution = call_user_func_array($solutionFunc, $solutionFuncArgs); + $this->checkSolution($name, $solution); - $this->solutions[$name] = $solution; + return $solution; + }; $this->solutionOptions[$name] = $options; return $this; @@ -44,7 +49,7 @@ public function addSolution($name, $solution, $options = []) */ public function getSolution($name) { - return isset($this->solutions[$name]) ? $this->solutions[$name] : null; + return isset($this->solutions[$name]) ? $this->loadSolution($name) : null; } /** @@ -52,7 +57,7 @@ public function getSolution($name) */ public function getSolutions() { - return $this->solutions; + return $this->loadSolutions(); } /** @@ -65,6 +70,44 @@ public function getSolutionOptions($name) return isset($this->solutionOptions[$name]) ? $this->solutionOptions[$name] : []; } + /** + * @param string $name + * + * @return mixed + */ + private function loadSolution($name) + { + if ($this->fullyLoadedSolutions[$name]) { + return $this->solutions[$name]; + } else { + $loader = $this->solutions[$name]; + $this->solutions[$name] = $loader(); + $this->fullyLoadedSolutions[$name] = true; + $this->postLoadSolution($this->solutions[$name]); + + return $this->solutions[$name]; + } + } + + /** + * @return mixed[] + */ + private function loadSolutions() + { + foreach ($this->solutions as $name => &$solution) { + $solution = $this->loadSolution($name); + } + + return $this->solutions; + } + + /** + * @param mixed $solution + */ + protected function postLoadSolution($solution) + { + } + /** * @param mixed $solution * @@ -77,6 +120,15 @@ protected function supportsSolution($solution) return null === $supportedClass || $solution instanceof $supportedClass; } + protected function checkSolution($name, $solution) + { + if (!$this->supportsSolution($solution)) { + throw new UnsupportedResolverException( + sprintf('Resolver "%s" must be "%s" "%s" given.', $name, $this->supportedSolutionClass(), get_class($solution)) + ); + } + } + /** * default return null to accept mixed type. * diff --git a/Resolver/ResolverInterface.php b/Resolver/ResolverInterface.php index 1d8dd7764..096e803b4 100644 --- a/Resolver/ResolverInterface.php +++ b/Resolver/ResolverInterface.php @@ -15,7 +15,7 @@ interface ResolverInterface { public function resolve($input); - public function addSolution($name, $solution, $extraOptions = []); + public function addSolution($name, callable $solutionFunc, array $solutionFuncArgs = [], array $options = []); public function getSolution($name); diff --git a/Resolver/TypeResolver.php b/Resolver/TypeResolver.php index 42c586c71..a138ed032 100644 --- a/Resolver/TypeResolver.php +++ b/Resolver/TypeResolver.php @@ -43,11 +43,6 @@ public function resolve($alias) $type = $this->string2Type($alias); $item->set($type); $this->cacheAdapter->save($item); - - // also add solution with real type name if needed for typeLoader when using autoMapping - if ($type && !isset($this->getSolutions()[$type->name])) { - $this->addSolution($type->name, $type); - } } return $item->get(); @@ -104,6 +99,16 @@ private function hasNeedListOfWrapper($alias) return false; } + protected function postLoadSolution($solution) + { + // also add solution with real type name if needed for typeLoader when using autoMapping + if ($solution && !isset($this->getSolutions()[$solution->name])) { + $this->addSolution($solution->name, function () use ($solution) { + return $solution; + }); + } + } + protected function supportedSolutionClass() { return Type::class; diff --git a/Tests/Resolver/AbstractProxyResolverTest.php b/Tests/Resolver/AbstractProxyResolverTest.php index b1d220bfe..56ec819ed 100644 --- a/Tests/Resolver/AbstractProxyResolverTest.php +++ b/Tests/Resolver/AbstractProxyResolverTest.php @@ -16,10 +16,15 @@ abstract class AbstractProxyResolverTest extends AbstractResolverTest protected function getResolverSolutionsMapping() { return [ - 'Toto' => ['solution' => new Toto(), 'method' => 'resolve'], + 'Toto' => ['solutionFunc' => [$this, 'createToto'], 'solutionFuncArgs' => [], 'method' => 'resolve'], ]; } + public function createToto() + { + return new Toto(); + } + public function testResolveKnownMutation() { $result = $this->resolver->resolve(['Toto', ['my', 'resolve', 'test']]); diff --git a/Tests/Resolver/AbstractResolverTest.php b/Tests/Resolver/AbstractResolverTest.php index 93735c030..2286f97c5 100644 --- a/Tests/Resolver/AbstractResolverTest.php +++ b/Tests/Resolver/AbstractResolverTest.php @@ -27,7 +27,7 @@ public function setUp() $this->resolver = $this->createResolver(); foreach ($this->getResolverSolutionsMapping() as $name => $options) { - $this->resolver->addSolution($name, $options['solution'], $options); + $this->resolver->addSolution($name, $options['solutionFunc'], $options['solutionFuncArgs'], $options); } } } diff --git a/Tests/Resolver/TypeResolverTest.php b/Tests/Resolver/TypeResolverTest.php index 5e460e835..f8cefd873 100644 --- a/Tests/Resolver/TypeResolverTest.php +++ b/Tests/Resolver/TypeResolverTest.php @@ -26,18 +26,26 @@ protected function createResolver() protected function getResolverSolutionsMapping() { return [ - 'Toto' => ['solution' => new ObjectType(['name' => 'Toto'])], - 'Tata' => ['solution' => new ObjectType(['name' => 'Tata'])], + 'Toto' => ['solutionFunc' => [$this, 'createObjectType'], 'solutionFuncArgs' => [['name' => 'Toto']]], + 'Tata' => ['solutionFunc' => [$this, 'createObjectType'], 'solutionFuncArgs' => [['name' => 'Tata']]], ]; } + public function createObjectType(array $config) + { + return new ObjectType($config); + } + /** * @expectedException \Overblog\GraphQLBundle\Resolver\UnsupportedResolverException * @expectedExceptionMessage Resolver "not-supported" must be "GraphQL\Type\Definition\Type" "stdClass" given. */ public function testAddNotSupportedSolution() { - $this->resolver->addSolution('not-supported', new \stdClass()); + $this->resolver->addSolution('not-supported', function () { + return new \stdClass(); + }); + $this->resolver->getSolution('not-supported'); } public function testResolveKnownType()