diff --git a/CHANGELOG.md b/CHANGELOG.md index 846c53179..e1fd1c740 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ You can find and compare releases at the [GitHub release page](https://github.co ## Unreleased +## v15.13.0 + +### Added + +- Serialize valid `\BackedEnum` values in `GraphQL\Type\Definition\PhpEnumType` https://github.com/webonyx/graphql-php/pull/1604 + ## v15.12.5 ### Fixed diff --git a/src/Type/Definition/PhpEnumType.php b/src/Type/Definition/PhpEnumType.php index 857b12e88..e61736efa 100644 --- a/src/Type/Definition/PhpEnumType.php +++ b/src/Type/Definition/PhpEnumType.php @@ -6,9 +6,7 @@ use GraphQL\Utils\PhpDoc; use GraphQL\Utils\Utils; -/** - * @phpstan-import-type PartialEnumValueConfig from EnumType - */ +/** @phpstan-import-type PartialEnumValueConfig from EnumType */ class PhpEnumType extends EnumType { public const MULTIPLE_DESCRIPTIONS_DISALLOWED = 'Using more than 1 Description attribute is not supported.'; @@ -47,12 +45,23 @@ public function __construct(string $enum, ?string $name = null) public function serialize($value): string { - if (! ($value instanceof $this->enumClass)) { - $notEnum = Utils::printSafe($value); - throw new SerializationError("Cannot serialize value as enum: {$notEnum}, expected instance of {$this->enumClass}."); + if ($value instanceof $this->enumClass) { + return $value->name; + } + + if (is_a($this->enumClass, \BackedEnum::class, true)) { + try { + $instance = $this->enumClass::from($value); + } catch (\ValueError|\TypeError $_) { + $notEnumInstanceOrValue = Utils::printSafe($value); + throw new SerializationError("Cannot serialize value as enum: {$notEnumInstanceOrValue}, expected instance or valid value of {$this->enumClass}."); + } + + return $instance->name; } - return $value->name; + $notEnum = Utils::printSafe($value); + throw new SerializationError("Cannot serialize value as enum: {$notEnum}, expected instance of {$this->enumClass}."); } public function parseValue($value) diff --git a/tests/Type/PhpEnumTypeTest.php b/tests/Type/PhpEnumTypeTest.php index c9c2b4c97..dd26de485 100644 --- a/tests/Type/PhpEnumTypeTest.php +++ b/tests/Type/PhpEnumTypeTest.php @@ -136,6 +136,28 @@ public function testExecutesWithEnumTypeFromPhpEnum(): void ], GraphQL::executeQuery($schema, '{ foo(bar: A) }')->toArray()); } + public function testSerializesBackedEnumsByValue(): void + { + $enumType = new PhpEnumType(IntPhpEnum::class); + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'foo' => [ + 'type' => Type::nonNull($enumType), + 'resolve' => static fn (): int => 1, + ], + ], + ]), + ]); + + self::assertSame([ + 'data' => [ + 'foo' => 'A', + ], + ], GraphQL::executeQuery($schema, '{ foo }')->toArray()); + } + public function testAcceptsEnumFromVariableValues(): void { $enumType = new PhpEnumType(PhpEnum::class); @@ -212,4 +234,25 @@ public function testFailsToSerializeNonEnum(): void self::expectExceptionObject(new SerializationError('Cannot serialize value as enum: "A", expected instance of GraphQL\\Tests\\Type\\PhpEnumType\\PhpEnum.')); $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS); } + + public function testFailsToSerializeNonEnumValue(): void + { + $enumType = new PhpEnumType(IntPhpEnum::class); + $schema = new Schema([ + 'query' => new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'foo' => [ + 'type' => Type::nonNull($enumType), + 'resolve' => static fn (): string => 'A', + ], + ], + ]), + ]); + + $result = GraphQL::executeQuery($schema, '{ foo }'); + + self::expectExceptionObject(new SerializationError('Cannot serialize value as enum: "A", expected instance or valid value of GraphQL\\Tests\\Type\\PhpEnumType\\IntPhpEnum.')); + $result->toArray(DebugFlag::RETHROW_INTERNAL_EXCEPTIONS); + } }