From 5d867cc40b2f924ba92abac1b912c3cdaf4ce028 Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 14:16:48 -0600 Subject: [PATCH 1/4] Moved all extract operations into HydratorExtractTool --- src/ConfigProvider.php | 5 ++ src/Criteria/FilterTypeAbstractFactory.php | 19 ++--- src/Field/FieldResolver.php | 13 ++-- src/Field/FieldResolverFactory.php | 4 +- src/Filter/FilterTypeAbstractFactory.php | 19 ++--- src/Hydrator/HydratorExtractToolDefault.php | 75 +++++++++++++++++++ .../HydratorExtractToolDefaultFactory.php | 18 +++++ src/Hydrator/HydratorExtractToolInterface.php | 9 +++ src/Resolve/EntityResolveAbstractFactory.php | 22 +++--- src/Type/EntityTypeAbstractFactory.php | 31 ++++---- test/GraphQL/EventsTest.php | 22 +++--- 11 files changed, 164 insertions(+), 73 deletions(-) create mode 100644 src/Hydrator/HydratorExtractToolDefault.php create mode 100644 src/Hydrator/HydratorExtractToolDefaultFactory.php create mode 100644 src/Hydrator/HydratorExtractToolInterface.php diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 4c4a5e1..ec63dbd 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -29,7 +29,12 @@ public function __invoke() public function getDependencyConfig() { return [ + 'aliases' => [ + 'ZF\Doctrine\GraphQL\Hydrator\HydratorExtractTool' => Hydrator\HydratorExtractToolDefault::class, + ], 'factories' => [ + Hydrator\HydratorExtractToolDefault::class => Hydrator\HydratorExtractToolDefaultFactory::class, + Hydrator\Filter\FilterDefault::class => InvokableFactory::class, Hydrator\Filter\Password::class => InvokableFactory::class, Hydrator\Strategy\ToBoolean::class => InvokableFactory::class, diff --git a/src/Criteria/FilterTypeAbstractFactory.php b/src/Criteria/FilterTypeAbstractFactory.php index 95a4e2a..fa14985 100644 --- a/src/Criteria/FilterTypeAbstractFactory.php +++ b/src/Criteria/FilterTypeAbstractFactory.php @@ -53,21 +53,18 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $config = $container->get('config'); $fields = []; - $hydratorManager = $container->get('HydratorManager'); $typeManager = $container->get(TypeManager::class); $filterManager = $container->get(FilterManager::class); $orderByManager = $container->get(OrderByManager::class); - $hydratorAlias = 'ZF\\Doctrine\\GraphQL\\Hydrator\\' . str_replace('\\', '_', $requestedName); - $hydratorConfig = $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]; - - $objectManager = $container->get($hydratorConfig['object_manager']); - $hydrator = $hydratorManager->get($hydratorAlias); - - // Create an instance of the entity in order to get fields from the hydrator. - $instantiator = new Instantiator(); - $entity = $instantiator->instantiate($requestedName); - $entityFields = array_keys($hydrator->extract($entity)); + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); + $objectManager = $container + ->get( + $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]['object_manager'] + ); + + // Get an array of the hydrator fields + $entityFields = $hydratorExtractTool->getFieldArray($requestedName, $hydratorAlias, $options); $classMetadata = $objectManager->getClassMetadata($requestedName); diff --git a/src/Field/FieldResolver.php b/src/Field/FieldResolver.php index 13ea541..0e5e3b2 100644 --- a/src/Field/FieldResolver.php +++ b/src/Field/FieldResolver.php @@ -9,13 +9,14 @@ use Doctrine\Common\Util\ClassUtils; use GraphQL\Type\Definition\ResolveInfo; use ZF\Doctrine\GraphQL\Context; +use ZF\Doctrine\GraphQL\Hydrator\HydratorExtractToolInterface; /** * A field resolver which uses the Doctrine hydrator. Can be used byReference or byValue. */ class FieldResolver { - private $hydratorManager; + private $hydratorExtractTool; /** * Cache all hydrator extract operations based on spl object hash @@ -24,9 +25,9 @@ class FieldResolver */ private $extractValues = []; - public function __construct(HydratorPluginManager $hydratorManager) + public function __construct(HydratorExtractToolInterface $hydratorExtractTool) { - $this->hydratorManager = $hydratorManager; + $this->hydratorExtractTool = $hydratorExtractTool; } public function __invoke($source, $args, Context $context, ResolveInfo $info) @@ -50,8 +51,7 @@ public function __invoke($source, $args, Context $context, ResolveInfo $info) $this->extractValues = []; } - $hydrator = $this->hydratorManager->get($hydratorAlias); - $this->extractValues[$splObjectHash] = $hydrator->extract($source); + $this->extractValues[$splObjectHash] = $this->hydratorExtractTool->extract($source, $hydratorAlias, $context); return $this->extractValues[$splObjectHash][$info->fieldName] ?? null; } @@ -61,8 +61,7 @@ public function __invoke($source, $args, Context $context, ResolveInfo $info) return $this->extractValues[$splObjectHash][$info->fieldName] ?? null; } - $hydrator = $this->hydratorManager->get($hydratorAlias); - $this->extractValues[$splObjectHash] = $hydrator->extract($source); + $this->extractValues[$splObjectHash] = $this->hydratorExtractTool->extract($source, $hydratorAlias, $context); return $this->extractValues[$splObjectHash][$info->fieldName] ?? null; } diff --git a/src/Field/FieldResolverFactory.php b/src/Field/FieldResolverFactory.php index ef38990..e716d7d 100644 --- a/src/Field/FieldResolverFactory.php +++ b/src/Field/FieldResolverFactory.php @@ -11,8 +11,8 @@ public function __invoke( $requestedName, array $options = null ) { - $hydratorManager = $container->get('HydratorManager'); + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); - return new FieldResolver($hydratorManager); + return new FieldResolver($hydratorExtractTool); } } diff --git a/src/Filter/FilterTypeAbstractFactory.php b/src/Filter/FilterTypeAbstractFactory.php index 24e65a3..259298e 100644 --- a/src/Filter/FilterTypeAbstractFactory.php +++ b/src/Filter/FilterTypeAbstractFactory.php @@ -5,7 +5,6 @@ use Interop\Container\ContainerInterface; use Zend\ServiceManager\AbstractFactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; -use Doctrine\Instantiator\Instantiator; use Doctrine\ORM\Mapping\MappingException; use GraphQL\Type\Definition\Type; use ZF\Doctrine\QueryBuilder\Filter\Service\ORMFilterManager; @@ -54,22 +53,20 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $fields = []; $config = $container->get('config'); - $hydratorManager = $container->get('HydratorManager'); $typeManager = $container->get(TypeManager::class); - $hydratorAlias = 'ZF\\Doctrine\\GraphQL\\Hydrator\\' . str_replace('\\', '_', $requestedName); - $hydratorConfig = $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]; - $hydrator = $hydratorManager->build($hydratorAlias, $options); - - $objectManager = $container->get($hydratorConfig['object_manager']); + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); + $objectManager = $container + ->get( + $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]['object_manager'] + ); $filterManager = $container->get(ORMFilterManager::class); $criteriaFilterManager = $container->get(CriteriaFilterManager::class); $orderByManager = $container->get(ORMOrderByManager::class); - // Create an instance of the entity in order to get fields from the hydrator. - $instantiator = new Instantiator(); - $entity = $instantiator->instantiate($requestedName); - $entityFields = array_keys($hydrator->extract($entity)); + // Get an array of the hydrator fields + $entityFields = $hydratorExtractTool->getFieldArray($requestedName, $hydratorAlias, $options); + $references = []; $classMetadata = $objectManager->getClassMetadata($requestedName); diff --git a/src/Hydrator/HydratorExtractToolDefault.php b/src/Hydrator/HydratorExtractToolDefault.php new file mode 100644 index 0000000..47bd323 --- /dev/null +++ b/src/Hydrator/HydratorExtractToolDefault.php @@ -0,0 +1,75 @@ +hydratorManager = $hydratorManager; + } + + // Extract an array of entities and return a collection + public function extractToCollection($entityArray, string $hydratorAlias, $options) + { + $options = $this->optionsToArray($options); + $hydrator = $this->hydratorManager->build($hydratorAlias, $options); + + $resultCollection = new ArrayCollection(); + foreach ($entityArray as $value) { + if (is_array($value)) { + $resultCollection->add($value); + } else { + $resultCollection->add($hydrator->extract($value)); + } + } + + return $resultCollection; + } + + // Extract a single entity + public function extract($entity, string $hydratorAlias, $options) + { + if (is_array($entity)) { + return $entity; + } + + $options = $this->optionsToArray($options); + $hydrator = $this->hydratorManager->build($hydratorAlias, $options); + + return $hydrator->extract($entity); + } + + public function getFieldArray(string $entityClassName, string $hydratorAlias, $options) + { + $instantiator = new Instantiator(); + $entity = $instantiator->instantiate($entityClassName); + + $options = $this->optionsToArray($options); + $hydrator = $this->hydratorManager->build($hydratorAlias, $options); + + return array_keys($hydrator->extract($entity)); + } + + private function optionsToArray($options) + { + if ($options instanceof Context) { + $options = $options->toArray($options); + } + + return $options; + } +} diff --git a/src/Hydrator/HydratorExtractToolDefaultFactory.php b/src/Hydrator/HydratorExtractToolDefaultFactory.php new file mode 100644 index 0000000..2ed0d53 --- /dev/null +++ b/src/Hydrator/HydratorExtractToolDefaultFactory.php @@ -0,0 +1,18 @@ +get('HydratorManager'); + + return new HydratorExtractToolDefault($hydratorManager); + } +} diff --git a/src/Hydrator/HydratorExtractToolInterface.php b/src/Hydrator/HydratorExtractToolInterface.php new file mode 100644 index 0000000..976d02c --- /dev/null +++ b/src/Hydrator/HydratorExtractToolInterface.php @@ -0,0 +1,9 @@ +createEventManager($container->get('SharedEventManager')); $config = $container->get('config'); - $hydratorManager = $container->get('HydratorManager'); $hydratorAlias = 'ZF\\Doctrine\\GraphQL\\Hydrator\\' . str_replace('\\', '_', $requestedName); - $hydrator = $hydratorManager->build($hydratorAlias, $options); - $hydratorConfig = $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]; + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); $filterManager = $container->get(ORMFilterManager::class); $orderByManager = $container->get(ORMOrderByManager::class); $criteriaFilterManager = $container->get(CriteriaFilterManager::class); $criteriaBuilder = $container->get(CriteriaBuilder::class); - $objectManager = $container->get($hydratorConfig['object_manager']); + $objectManager = $container + ->get( + $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]['object_manager'] + ); $instance = function ( $obj, @@ -96,7 +96,8 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $context ) use ( $options, - $hydrator, + $hydratorAlias, + $hydratorExtractTool, $objectManager, $requestedName, $filterManager, @@ -112,7 +113,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o 'object' => $obj, 'arguments' => $args, 'context' => $context, - 'hydrator' => $hydrator, + 'hydratorAlias' => $hydratorAlias, 'objectManager' => $objectManager, 'entityClassName' => $requestedName, ] @@ -290,10 +291,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $results = $queryBuilder->getQuery()->getResult(); // Build hydrated result collection - $resultCollection = new ArrayCollection(); - foreach ($results as $key => $value) { - $resultCollection->add($hydrator->extract($value)); - } + $resultCollection = $hydratorExtractTool->extractToCollection($results, $hydratorAlias, $options); // Criteria post filter if ($criteriaArray) { @@ -322,7 +320,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o 'arguments' => $args, 'context' => $context, 'resultCollection' => $resultCollection, - 'hydrator' => $hydrator, + 'hydratorAlias' => $hydratorAlias, 'objectManager' => $objectManager, 'queryBuilder' => $queryBuilder, 'entityClassName' => $requestedName, diff --git a/src/Type/EntityTypeAbstractFactory.php b/src/Type/EntityTypeAbstractFactory.php index 1035a8b..5624e0e 100644 --- a/src/Type/EntityTypeAbstractFactory.php +++ b/src/Type/EntityTypeAbstractFactory.php @@ -9,7 +9,6 @@ use Zend\ServiceManager\ServiceLocatorInterface; use Doctrine\Common\Util\ClassUtils; use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Instantiator\Instantiator; use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\MappingException; use GraphQL\Type\Definition\Type; @@ -62,21 +61,20 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $fieldMetadata = null; $fields = []; $config = $container->get('config'); - $hydratorManager = $container->get('HydratorManager'); $fieldResolver = $container->get(FieldResolver::class); $typeManager = $container->get(TypeManager::class); $criteriaFilterManager = $container->get(FilterManager::class); $criteriaBuilder = $container->get(CriteriaBuilder::class); - $hydratorAlias = 'ZF\\Doctrine\\GraphQL\\Hydrator\\' . str_replace('\\', '_', $requestedName); - $hydratorConfig = $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]; - $hydrator = $hydratorManager->build($hydratorAlias, $options); - $objectManager = $container->get($hydratorConfig['object_manager']); - - // Create an instance of the entity in order to get fields from the hydrator. - $instantiator = new Instantiator(); - $entity = $instantiator->instantiate($requestedName); - $entityFields = array_keys($hydrator->extract($entity)); + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); + $objectManager = $container + ->get( + $config['zf-doctrine-graphql-hydrator'][$hydratorAlias][$options['hydrator_section']]['object_manager'] + ); + + // Get an array of the hydrator fields + $entityFields = $hydratorExtractTool->getFieldArray($requestedName, $hydratorAlias, $options); + $references = []; $classMetadata = $objectManager->getClassMetadata($requestedName); @@ -112,7 +110,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $targetEntity, $objectManager, $criteriaBuilder, - $hydratorManager + $hydratorExtractTool ) { return [ 'type' => Type::listOf($typeManager->build($targetEntity, $options)), @@ -129,7 +127,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $fieldResolver, $objectManager, $criteriaBuilder, - $hydratorManager + $hydratorExtractTool ) { $collection = $fieldResolver($source, $args, $context, $resolveInfo); @@ -256,12 +254,9 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $entityClassName = ClassUtils::getRealClass(get_class($collection->first())); $hydratorAlias = 'ZF\\Doctrine\\GraphQL\\Hydrator\\' . str_replace('\\', '_', $entityClassName); - $hydrator = $hydratorManager->build($hydratorAlias, $options); - $data = new ArrayCollection(); - foreach ($collection as $key => $value) { - $data->add($hydrator->extract($value)); - } + $data = $hydratorExtractTool + ->extractToCollection($collection, $hydratorAlias, $options); $matching = $data->matching($criteria); diff --git a/test/GraphQL/EventsTest.php b/test/GraphQL/EventsTest.php index f4ef6d7..46dd40a 100644 --- a/test/GraphQL/EventsTest.php +++ b/test/GraphQL/EventsTest.php @@ -55,16 +55,17 @@ public function testResolveEvent($schemaName, $context) $container = $this->getApplication()->getServiceManager(); $events = $container->get('SharedEventManager'); + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); $events->attach( EntityResolveAbstractFactory::class, EntityResolveAbstractFactory::RESOLVE, - function(Event $event) + function(Event $event) use ($hydratorExtractTool) { $object = $event->getParam('object'); $arguments = $event->getParam('arguments'); $context = $event->getParam('context'); - $hydrator = $event->getParam('hydrator'); + $hydratorAlias = $event->getParam('hydratorAlias'); $objectManager = $event->getParam('objectManager'); $entityClassName = $event->getParam('entityClassName'); @@ -72,14 +73,11 @@ function(Event $event) 'attendance' => 2000, ]); - $matching = []; - foreach ($results as $key => $value) { - $matching[$key] = $hydrator->extract($value); - } + $resultCollection = $hydratorExtractTool->extractToCollection($results, $hydratorAlias, $context); $event->stopPropagation(true); - return $matching; + return $resultCollection; }, 100 ); @@ -101,26 +99,26 @@ public function testResolvePostEvent($schemaName, $context) $container = $this->getApplication()->getServiceManager(); $events = $container->get('SharedEventManager'); + $hydratorExtractTool = $container->get('ZF\\Doctrine\\GraphQL\\Hydrator\\HydratorExtractTool'); $events->attach( EntityResolveAbstractFactory::class, EntityResolveAbstractFactory::RESOLVE_POST, - function(Event $event) + function(Event $event) use ($hydratorExtractTool) { $objectManager = $event->getParam('objectManager'); $entityClassName = $event->getParam('entityClassName'); $resultCollection = $event->getParam('resultCollection'); $context = $event->getParam('context'); - $hydrator = $event->getParam('hydrator'); - - $resultCollection->clear(); + $hydratorAlias = $event->getParam('hydratorAlias'); $results = $objectManager->getRepository($entityClassName)->findBy([ 'attendance' => 2000, ]); + $resultCollection->clear(); foreach ($results as $key => $value) { - $resultCollection->add($hydrator->extract($value)); + $resultCollection->add($hydratorExtractTool->extract($value, $hydratorAlias, $context)); } $event->stopPropagation(true); From 497bc1c55239fabdcebfd3bdf68ffa24fa667839 Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 14:18:39 -0600 Subject: [PATCH 2/4] phpcs --- src/Field/FieldResolver.php | 3 ++- src/Hydrator/HydratorExtractToolDefault.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Field/FieldResolver.php b/src/Field/FieldResolver.php index 0e5e3b2..0963d57 100644 --- a/src/Field/FieldResolver.php +++ b/src/Field/FieldResolver.php @@ -51,7 +51,8 @@ public function __invoke($source, $args, Context $context, ResolveInfo $info) $this->extractValues = []; } - $this->extractValues[$splObjectHash] = $this->hydratorExtractTool->extract($source, $hydratorAlias, $context); + $this->extractValues[$splObjectHash] + = $this->hydratorExtractTool->extract($source, $hydratorAlias, $context); return $this->extractValues[$splObjectHash][$info->fieldName] ?? null; } diff --git a/src/Hydrator/HydratorExtractToolDefault.php b/src/Hydrator/HydratorExtractToolDefault.php index 47bd323..4035cb6 100644 --- a/src/Hydrator/HydratorExtractToolDefault.php +++ b/src/Hydrator/HydratorExtractToolDefault.php @@ -67,7 +67,7 @@ public function getFieldArray(string $entityClassName, string $hydratorAlias, $o private function optionsToArray($options) { if ($options instanceof Context) { - $options = $options->toArray($options); + $options = $options->toArray(); } return $options; From 0e0aca9dab5e99c5960e649cf4c4193944d46fa3 Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 14:24:21 -0600 Subject: [PATCH 3/4] Added internals section to README --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 9ec9bde..6780cdd 100644 --- a/README.md +++ b/README.md @@ -375,3 +375,23 @@ Resolve Post The `EntityResolveAbstractFactory::RESOLVE_POST` event allows you to modify the values returned from the ResolveLoader via an ArrayObject or replace the values. + + +Internals +========= + + +Hydrator Extract Tool +--------------------- + +All hydrator extract operations are handled through the Hydrator Extract Tool. This tool is engineered to be overridden +thanks to a service manager alias. Should you find the need to add custom caching to hydrator results this is where to +to it. + + +Field Resolver +-------------- + +This standard part of GraphQL resolves individual fields and is where the built in caching resides. This resolver uses +the Hydrator Extract Tool and returns one field value at a time. For high performance writing your own Field Resolver is an +option. To register a custom field resolver use `GraphQL::setDefaultFieldResolver($fieldResolver);` From 90684841852f7d8eeef28714af8c74373ccc77d7 Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 14:27:55 -0600 Subject: [PATCH 4/4] Added internals section to README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6780cdd..e34cf19 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,12 @@ Hydrator Extract Tool All hydrator extract operations are handled through the Hydrator Extract Tool. This tool is engineered to be overridden thanks to a service manager alias. Should you find the need to add custom caching to hydrator results this is where to -to it. +do it. To register a custom hydrator extract tool use +```php + 'aliases' => [ + 'ZF\Doctrine\GraphQL\Hydrator\HydratorExtractTool' => CustomExtractTool::class, + ], +``` Field Resolver