In order to respond to queries, a schema needs to have resolve functions for all fields. Resolve functions cannot be included in the GraphQL schema language, so they must be added separately. This collection of functions is called the "resolver map".
A resolverMap is simple an object implementing Overblog\GraphQLBundle\Resolver\ResolverMapInterface
you can also just extend the concrete class Overblog\GraphQLBundle\Resolver\ResolverMap
and override map method and return an array or any ArrayAccess and Traversable implementation.
- The
Overblog\GraphQLBundle\Resolver\ResolverMapInterfaceexposes three methods:resolve,isResolvableandcovered. It also exposes constants representing some specials config fields. resolvetakes two mandatory parameters: the type name and the config field name to resolve, which MUST be strings.resolvecan return anything (a mixed value), or throw aOverblog\GraphQLBundle\UnresolvableExceptionif the resolver for type name and config field name is not known to the resolverMap.isResolvabletakes two parameters: the type name and the config field name to resolve, which MUST be strings.isResolvableMUST return true if the resolver for type name and config field name is known to the resolverMap and false if it is not. IfisResolvable($typeName, $fieldName)returns false,resolve($typeName, $fieldName)MUST throw aOverblog\GraphQLBundle\UnresolvableException.coveredtakes unique optional parameter: the type name to resolve, which MUST be strings.coveredMUST return an array of the names of the types covered if$typeNameequal to null or return the type fields covered. Ifcovered($typeName)returns an empty array or/and the fieldName is not present in array,resolve($typeName, $fieldName)MUST throw aOverblog\GraphQLBundle\UnresolvableException.- constants (specials config fields):
- Union and Interface types
Overblog\GraphQLBundle\Resolver\ResolverMapInterface::RESOLVE_TYPEequivalent toresolveType.
- Object type
Overblog\GraphQLBundle\Resolver\ResolverMapInterface::RESOLVE_FIELDequivalent toresolveField.Overblog\GraphQLBundle\Resolver\ResolverMapInterface::IS_TYPE_OFequivalent toisTypeOf.
- Custom scalar type
- Direct usage:
Overblog\GraphQLBundle\Resolver\ResolverMapInterface::SERIALIZEequivalent toserializeOverblog\GraphQLBundle\Resolver\ResolverMapInterface::PARSE_VALUEequivalent toparseValueOverblog\GraphQLBundle\Resolver\ResolverMapInterface::PARSE_LITERALequivalent toparseLiteral
- Reusing an existing scalar type
Overblog\GraphQLBundle\Resolver\ResolverMapInterface::SCALAR_TYPEequivalent toscalarType
- Direct usage:
- Union and Interface types
The following is an example of a valid resolverMap object that can be be used for GraphQL schema language :
<?php
namespace App\Resolver;
use GraphQL\Error\Error;
use GraphQL\Language\AST\StringValueNode;
use GraphQL\Type\Definition\ResolveInfo;
use Overblog\GraphQLBundle\Definition\ArgumentInterface;
use Overblog\GraphQLBundle\Resolver\ResolverMap;
class MyResolverMap extends ResolverMap
{
protected function map()
{
return [
'Query' => [
self::RESOLVE_FIELD => function ($value, ArgumentInterface $args, \ArrayObject $context, ResolveInfo $info) {
if ('baz' === $info->fieldName) {
$id = (int) $args['id'];
return findBaz('baz', $id);
}
return null;
},
'bar' => [Bar::class, 'getBar'],
],
'Foo' => [
self::RESOLVE_TYPE => function ($value) {
return isset($value['user']) ? 'Bar' : null;
},
],
// enum internal values
'User' => [
'TATA' => 1,
'TITI' => 2,
'TOTO' => 3,
],
// custom scalar
'Baz' => [
self::SERIALIZE => function ($value) {
return sprintf('%s Formatted Baz', $value);
},
self::PARSE_VALUE => function ($value) {
if (!is_string($value)) {
throw new Error(sprintf('Cannot represent following value as a valid Baz: %s.', Utils::printSafeJson($value)));
}
return str_replace(' Formatted Baz', '', $value);
},
self::PARSE_LITERAL => function ($valueNode) {
if (!$valueNode instanceof StringValueNode) {
throw new Error('Query error: Can only parse strings got: '.$valueNode->kind, [$valueNode]);
}
return str_replace(' Formatted Baz', '', $valueNode->value);
},
],
// or reuse an existing scalar (note: description and name will be override by decorator)
//'Baz' => [self::SCALAR_TYPE => function () { return new FooScalarType(); }],
];
}
}Each resolver map must be tagged with the overblog_graphql.resolver_map tag
that defines at which priority it should run for the given schema. The priority
is an optional attribute and it has a default value of 0. The higher the number,
the earlier the resolver map is executed.
# config/services.yaml
services:
App\Resolver\MyResolverMap1:
tags:
- { name: overblog_graphql.resolver_map, schema: default }
App\Resolver\MyResolverMap2:
tags:
- { name: overblog_graphql.resolver_map, schema: default, priority: 10 }Notes:
- ResolverMap will override all matching entries when decorating types.
- ResolverMap does not supports
access,publicandquery complexityright now. - Many resolver map can be set for the same schema.
In this case the first resolverMap in list where
isResolvablereturnstruewill be use. - You don’t need to specify resolvers for every type in your schema. If you don’t specify a resolver, GraphQL falls back to a default one.
This feature was inspired by Apollo GraphQL tools.
Next step solving N+1 problem