Skip to content

Latest commit

 

History

History
243 lines (191 loc) · 6.47 KB

resolver.md

File metadata and controls

243 lines (191 loc) · 6.47 KB

Resolver

To ease development we created 2 types of resolver:

  • Query that should be used for resolving readonly actions
  • Mutation that should be used for resolving writing actions

This is just a recommendation.

Resolvers can be defined in 2 different ways:

The PHP way

You can declare a resolver (any class that implements Overblog\GraphQLBundle\Definition\Resolver\QueryInterface or Overblog\GraphQLBundle\Definition\Resolver\MutationInterface) in src/*Bundle/GraphQL or app/GraphQL and they will be auto discovered. Auto map classes method are accessible by:

  • double-colon (::) to separate service id (class name) and the method names (example: AppBunble\GraphQL\CustomResolver::myMethod)
  • for callable classes you can use the service id (example: AppBunble\GraphQL\InvokeResolver for a resolver implementing the __invoke method) you can also alias a type by implementing Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface which returns a map of method/alias. The service created will autowire the __construct and Symfony\Component\DependencyInjection\ContainerAwareInterface::setContainer methods.

Note:

  • When using service id as FQCN in yaml or annotation definition, backslashes must be correctly escaped, here an example: '@=query("App\\GraphQL\\Resolver\\Greetings", args["name"])'.
  • You can also see the more straight forward way using resolver map.

Resolver

Example using an alias:

resolve: '@=query("say_hello", args["name"])'
<?php
# src/GraphQL/Resolver/Greetings.php
namespace App\GraphQL\Resolver;

use Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface;
use Overblog\GraphQLBundle\Definition\Resolver\QueryInterface;

class Greetings implements QueryInterface, AliasedInterface
{
    public function sayHello($name)
    {
        return sprintf('hello %s!!!', $name);
    }

    /**
     * {@inheritdoc}
     */
    public static function getAliases(): array
    {
        return ['sayHello' => 'say_hello'];
    }
}

Example using a fully qualified method name:

resolve: '@=query("App\\GraphQL\\Resolver\\Greetings::sayHello", args["name"])'

Note: backslashes must be correctly escaped and respect the use of single and double quotes.

<?php
# src/GraphQL/Resolver/Greetings.php
namespace App\GraphQL\Resolver;

use Overblog\GraphQLBundle\Definition\Resolver\QueryInterface;

class Greetings implements QueryInterface
{
    public function sayHello($name)
    {
        return sprintf('hello %s!!!', $name);
    }
}

Example using the class invoker:

resolve: '@=query("App\\GraphQL\\Resolver\\Greetings", args["name"])'
<?php
# src/GraphQL/Resolver/Greetings.php
namespace App\GraphQL\Resolver;

use Overblog\GraphQLBundle\Definition\Resolver\QueryInterface;

class Greetings implements QueryInterface
{
    public function __invoke($name)
    {
        return sprintf('hello %s!!!', $name);
    }
}

This way SayHello resolver can be accessed with App\GraphQL\Resolver\Greetings.

You may also use the invoker to define a type-wide resolver with the resolveField option:

# config/graphql/types/MyType.types.yaml
MyType:
    type: object
    config:
        resolveField: '@=query("App\\GraphQL\\Resolver\\Greetings", info, args.name)'
        fields:
            hello:
                type: String
            goodbye:
                type: String
<?php
# src/GraphQL/Resolver/Greetings.php
namespace App\GraphQL\Resolver;

use GraphQL\Type\Definition\ResolveInfo;
use Overblog\GraphQLBundle\Definition\Resolver\QueryInterface;

class Greetings implements QueryInterface
{
    public function __invoke(ResolveInfo $info, $name)
    {
        if($info->fieldName === 'hello'){
            return sprintf('hello %s!!!', $name);
        }
        else if($info->fieldName === 'goodbye'){
            return sprintf('goodbye %s!!!', $name);
        }
        else{
            throw new \DomainException('Unknown greetings');
        }
    }
}

Mutation

<?php
# src/GraphQL/Mutation/CalcMutation.php
namespace App\GraphQL\Mutation;

use Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface;
use Overblog\GraphQLBundle\Definition\Resolver\MutationInterface;

class CalcMutation implements MutationInterface, AliasedInterface
{
    private $value;

    public function addition($number)
    {
        $this->value += $number;
    }

    /**
     * {@inheritdoc}
     */
    public static function getAliases(): array
    {
        return ['addition' => 'add'];
    }
}

addition mutation can be access by using App\GraphQL\Mutation\CalcMutation::addition or add alias.

Here an example of how this can be done with DI autoconfigure:

services:
    _defaults:
        autoconfigure: true

    Overblog\GraphQLBundle\GraphQL\Relay\:
        resource: ../../GraphQL/Relay/{Mutation,Node}

The service way

Creating a service tagged overblog_graphql.query for queries or overblog_graphql.mutation for mutations.

Using the php way examples:

services:
    App\GraphQL\Resolver\Greetings:
        # only for sf < 3.3
        #class: App\GraphQL\Resolver\Greetings
        tags:
            - { name: overblog_graphql.query, method: sayHello, alias: say_hello } # add alias say_hello
            - { name: overblog_graphql.query, method: sayHello } # add service id "App\GraphQL\Resolver\Greetings"

SayHello resolver can be access by using App\GraphQL\Resolver\Greetings::sayHello or say_hello alias.

for invokable classes no need to use alias and method attributes:

services:
    App\GraphQL\Resolver\Greetings:
        # only for sf < 3.3
        #class: App\GraphQL\Resolver\Greetings
        tags:
            - { name: overblog_graphql.query }

This way resolver can be accessed with service id App\GraphQL\Resolver\Greetings.

for mutation:

services:
    App\GraphQL\Mutation\CalcMutation:
        # only for sf < 3.3
        #class: App\GraphQL\Mutation\CalcMutation
        tags:
            - { name: overblog_graphql.mutation, method: addition, alias: add }

addition mutation can be access by using App\GraphQL\Mutation\CalcMutation::addition or add alias.

Default field resolver

The default field resolver can be define using config:

overblog_graphql:
    definitions:
       default_field_resolver: 'my_default_field_resolver_service'

Default field resolver should be a callable service (implementing __invoke method)

Next step solving N+1 problem