-
Notifications
You must be signed in to change notification settings - Fork 223
Description
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? | no |
Version/Branch | 0.11.10 |
I really struggle with the Resolver map and how I should organize things.
I'd like to abstract some of the work to reduce verbosity.
My goal is to obtain something as straightforward as the following:
type Query {
user(id: ID!): User
}
type User {
id: ID!
email: String!
firstName: String!
lastName: String!
avatar: Avatar!
}
type Avatar {
url: String!
placeholder: Boolean!
}
<?php
namespace ST\GraphQL\Resolver;
use Overblog\GraphQLBundle\Resolver\ResolverMap;
use GraphQL\Type\Definition\ResolveInfo;
use Overblog\GraphQLBundle\Definition\Argument;
class MainResolverMap extends ResolverMap
{
private $queryResolver;
private $userResolver;
public function __construct(QueryResolver $queryResolver, UserResolver $userResolver)
{
$this->queryResolver = $queryResolver;
$this->userResolver = $userResolver;
}
protected function map()
{
return [
'Query' => $this->queryResolver,
'User' => $this->userResolver,
];
}
}
<?php
namespace ST\GraphQL\Resolver;
use Overblog\GraphQLBundle\Definition\Argument;
use GraphQL\Type\Definition\ResolveInfo;
use ST\GraphQL\DataFetching\DataLoader;
class UserResolver
{
private $dataLoader;
public function __construct(DataLoader $dataLoader)
{
$this->dataLoader = $dataLoader;
}
public function resolveAvatar($value, Argument $args, \ArrayObject $context, ResolveInfo $info): array
{
// this would use some injected dependency
return [
'url' => 'http://....' . $value['id'] . '.jpg',
'placeholder' => false,
];
}
}
But because of the way the resolver map works, I can't figure out how to implement it this way.
Note that the following works:
class MainResolverMap extends ResolverMap
{
private $userResolver;
public function __construct(UserResolver $userResolver)
{
$this->userResolver = $userResolver;
}
protected function map()
{
return [
'Query' => ...,
'User' => [
'avatar' => function ($value, Argument $args, \ArrayObject $context, ResolveInfo $info) {
return $this->userResolver->resolveAvatar($value, $args, $context, $info);
}
]
];
}
}
But it's clearly very verbose and would become a pain to maintain on a large project!
I could move the User
field map inside the UserResolver class, but still, it's too much verbosity.
Also tried some naive approaches like the following:
abstract class AbstractTypeResolver
{
public function getResolveMap(): array
{
return [
ResolverMap::RESOLVE_FIELD => function ($value, Argument $args, \ArrayObject $context, ResolveInfo $info) {
$resolveMethod = 'resolve' . \ucfirst($info->fieldName);
if (\method_exists($this, $resolveMethod)) {
return call_user_func([$this, $resolveMethod], $value, $args, $context, $info);
}
// Not good
return $value[$info->fieldName];
}
];
}
}
class MainResolverMap extends ResolverMap
{
private $queryResolver;
private $userResolver;
public function __construct(QueryResolver $queryResolver, UserResolver $userResolver)
{
$this->queryResolver = $queryResolver;
$this->userResolver = $userResolver;
}
protected function map()
{
return [
'Query' => $this->queryResolver->getResolveMap(),
'User' => $this->userResolver->getResolveMap(),
];
}
}
And then having UserResolver
extending AbstractTypeResolver
but it doesn't seem right.
In the end I just want to avoid having to repeat that a million time:
'avatar' => function ($value, Argument $args, \ArrayObject $context, ResolveInfo $info) {
return $this->userResolver->resolveAvatar($value, $args, $context, $info);
}
And factor this logic somewhere hidden.
Any thoughts or recommendations?