graphql-php icon indicating copy to clipboard operation
graphql-php copied to clipboard

Pass custom serialize function to EnumType via config

Open oligus opened this issue 3 years ago • 5 comments

Is it possible to have custom enum types, like myclabs/php-enum?

It is possible via TypeConfigDecorator to replace enum values with php-enum, but the serialize function is not overridden from what I can see. The serialize function is never invoked, instead the default serialize function in EnumType is run. So I guess the question is how I can serialize custom enum types?

if ($typeConfig['astNode'] instanceof EnumTypeDefinitionNode) {
    $typeConfig['values'] = EnumValueMapper::map($typeConfig); // returns array of key => enum object 
    $typeConfig = array_merge($typeConfig, [
        'serialize' => function() {
            die('serialize');
        }
    ]);

    return $typeConfig;
}

oligus avatar Feb 05 '21 11:02 oligus

In Lighthouse, we have extended the EnumType to be a wrapper around bensampo/laravel-enum:

  • Implementation: https://github.com/nuwave/lighthouse/blob/master/src/Schema/Types/LaravelEnumType.php
  • Usage: https://github.com/nuwave/lighthouse/blob/master/tests/Unit/Schema/Types/LaravelEnumTypeTest.php

I hope that gives you some idea on how you can implement such a type yourself.

spawnia avatar Feb 05 '21 11:02 spawnia

Thank you for quick answer! I think you lost me (or me you) :-)

I have a schema file schema.graphql that I read together with TypeConfigDecorator:

$schema = BuildSchema::build($schemaFile, TypeConfigDecorator::resolve());

After which I run the GraphQL::executeQuery with a root resolver. With a programatically built schema, no problem to override the enums at all, similar to your approach with a type registry, but reading the schema directly I don't understand how to override the enums.

It might be worth to mention that I can override scalars in the TypeConfigDecorator with serialize, parseValue, parseLiteral functions that override the original function.

oligus avatar Feb 05 '21 13:02 oligus

I suppose that what you are trying to do is not possible with the type config decorator. It looks like the reference implementation does not support passing a custom serialize function through the config; since we usually follow them we don't either. https://graphql.org/graphql-js/type/#graphqlenumtype That is why I went with a subclass to allow for a custom serialize method.

Let's turn this into a feature request then, since I do think it is reasonable. I do like functional composition over inheritance anyways. Do you care enough to try and implement a PR? EnumType and the corresponding test would need to be adapted.

spawnia avatar Feb 05 '21 13:02 spawnia

Thank you for your reply. Let me take a look at it in the coming days and I can come back with my findings on this thread.

oligus avatar Feb 05 '21 14:02 oligus

For anyone just using this project.. using @spawnia 's pattern above this is a simple extended class to handle this:

use GraphQL\Type\Definition\EnumType as GraphQLEnumType;
use BenSampo\Enum\Enum as BenSampoEnum;
use InvalidArgumentException;

// https://github.com/webonyx/graphql-php/issues/775
// https://github.com/nuwave/lighthouse/blob/master/src/Schema/Types/LaravelEnumType.php

class EnumClassType extends GraphQLEnumType
{
    protected $enumClass;

    public function __construct($enumClass, $config)
    {
        if (! is_subclass_of($enumClass, BenSampoEnum::class)) {
            throw new InvalidArgumentException(
                "Must pass an instance of \BenSampo\Enum\Enum, got {$enumClass}."
            );
        }

        $this->enumClass = $enumClass;

        parent::__construct($config);
    }

    public function serialize($value): string
    {
        if (! $value instanceof BenSampoEnum) {
            $value = $this->enumClass::fromValue($value);
        }

        return $value->key;
    }
}

ctadlock avatar Nov 17 '21 08:11 ctadlock