webonyx / graphql-php

PHP implementation of the GraphQL specification based on the reference implementation in JavaScript
https://webonyx.github.io/graphql-php
MIT License
4.64k stars 562 forks source link

Define field resolver using `SchemaExtender` #1588

Open Warxcell opened 2 months ago

Warxcell commented 2 months ago

Title is basically self explanatory.

Fields created from SchemaExtender will always have NULL resolver. - typeConfigDecorator is not called when called ASTDefinitionBuilder::buildField() - and that's pretty annoying. Work-around is to attach to resolveField gateway function which catches all fields in order to fix that - which is pretty annoying.

spawnia commented 2 months ago

I am not familiar with that part of the API and what you might need there. Can you provide a pull request?

Warxcell commented 2 months ago

If we have following code:

$resolvers = [
   'Query' => [
      'field1' => fn() => 'string',
      'field2' => fn() => 123',
   ]
];

$typeConfigDecorator = static function (array $typeConfig, TypeDefinitionNode $typeDefinitionNode) use ($resolvers) {
   if($typeConfig['name'] === 'Query') {
       $fields = $typeConfig['fields'];

      $typeConfig['fields'] = static function() use ($fields) {
         $fields = $fields();

         foreach($fields as &$field) {
             $field['resolve'] = $resolvers[$name][$field['name']];
         }
      };
   }

   return $typeConfig;
};

$sdl = Parser::parse(`
type Query {
   field1: String!
}
`);

$schema = BuildSchema::build(
   $sdl
    $typeConfigDecorator,
    $options
);

$extenderSdl = Parser::parse(`
   extend type Query {
      field2: Int!
  }
`)
SchemaExtender::extend(
            $schema,
            $extenderSdl,
            $options,
            $typeConfigDecorator
        );

field2 will never receive it's resolver. In order to fix that I have to create gateway resolver that is attached to the object rather than per field resolve function, like that:

$typeConfigDecorator = static function (array $typeConfig, TypeDefinitionNode $typeDefinitionNode) use ($resolvers) {
   if($typeConfig['name'] === 'Query') {
     $objectResolvers = $resolvers[$typeConfig['name']];
     $typeConfig['resolveField'] = static function(mixed $objectValue, mixed $args, mixed $contextValue, ResolveInfo $info) use ($objectResolvers) {
         return $objectResolvers[$info->fieldName]($objectValue, $args, $contextValue, $info);
     };
   }

   return $typeConfig;
};

Basically there is no way to set field2 'resolve' option when using SchemaExtender.