Skip to content

Conversation

ruudk
Copy link
Collaborator

@ruudk ruudk commented Oct 3, 2025

Note

This is an alternative (and I think better) approach to:

This allows transforming the object value after type resolution.

This is useful when you have a single entity that needs different representations. For example, when your data layer returns a generic PetEntity with a type discriminator, but your GraphQL schema has separate Dog and Cat types.

Example:

$PetType = new InterfaceType([
    'name' => 'Pet',
    'resolveType' => static function (PetEntity $objectValue): string {
        if ($objectValue->type === 'dog') {
            return 'Dog';
        }
        return 'Cat';
    },
    'resolveValue' => static function (PetEntity $objectValue) {
        if ($objectValue->type === 'dog') {
            return new Dog($objectValue->name, $objectValue->woofs);
        }
        return new Cat($objectValue->name, $objectValue->meows);
    },
    'fields' => ['name' => ['type' => Type::string()]],
]);

Now field resolvers receive the properly typed Dog or Cat object instead of the generic PetEntity,
allowing for type-safe resolution without needing to transform objects before they reach the type
resolver.

Common use cases:

  • Database polymorphism (single table with type column)
  • External APIs returning generic objects with type discriminators

@ruudk
Copy link
Collaborator Author

ruudk commented Oct 3, 2025

Instead of calling it resolveValue we could also go with transformValue indicating that this is about transforming the value from A to B.

This allows transforming the object value after type resolution.

This is useful when you have a single entity that needs different representations. For example, when
your data layer returns a generic `PetEntity` with a type discriminator, but your GraphQL schema has
separate `Dog` and `Cat` types.

Example:

```php
$PetType = new InterfaceType([
    'name' => 'Pet',
    'resolveType' => static function (PetEntity $objectValue): string {
        if ($objectValue->type === 'dog') {
            return 'Dog';
        }
        return 'Cat';
    },
    'resolveValue' => static function (PetEntity $objectValue) {
        if ($objectValue->type === 'dog') {
            return new Dog($objectValue->name, $objectValue->woofs);
        }
        return new Cat($objectValue->name, $objectValue->meows);
    },
    'fields' => ['name' => ['type' => Type::string()]],
]);

Now field resolvers receive the properly typed Dog or Cat object instead of the generic PetEntity,
allowing for type-safe resolution without needing to transform objects before they reach the type
resolver.

Common use cases:
- Database polymorphism (single table with type column)
- External APIs returning generic objects with type discriminators
@ruudk ruudk force-pushed the introduce-resolve-value branch from 26e8d73 to 80ba591 Compare October 3, 2025 12:11
@ruudk ruudk requested review from simPod and spawnia October 6, 2025 18:40
Copy link
Collaborator

@spawnia spawnia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name resolveValue makes sense to me, especially considering the symmetry with resolveType.


public string $name;

public bool $woofs;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vocalizes

$contextValue
) {
$typeCandidate = $returnType->resolveType($result, $contextValue, $info);
$result = $returnType->resolveValue($result, $contextValue, $info);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on with the pass-by-reference of $result? What happens if we remove &?

Also, what is going on with its type - we seem to know it is array<mixed> here, but is it really? What if me make it a hard type hint?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants