Skip to content

Conversation

@sustmi
Copy link

@sustmi sustmi commented Nov 13, 2024

(Sorry for explaining the PR by example with another project but I do not use php-code-generator directly.)
EDIT: Check my comment #21 (comment) below for much simpler example.

I am using graphql:compile command from overblog/graphql-bundle (https://github.com/overblog/GraphQLBundle/blob/master/docs/index.md) which internally uses php-code-generator.

When I ran composer graphql:compile I got the following error:

[Exception]                                                                       
Cannot stringify object of class: 'FrontBundle\Enum\Profile\ProfileOutlinkType'.  

Exception trace:
  at vendor/murtukov/php-code-generator/src/Utils.php:113
  ...
  Murtukov\PHPCodeGenerator\PhpFile->save() at vendor/overblog/graphql-bundle/src/Generator/TypeGenerator.php:93

I had GraphQL types defined as follows:

namespace MyApp;

use Overblog\GraphQLBundle\Annotation as GQL;

/**
 * @GQL\Enum(name="LinkType")
 */
enum LinkType: string 
{
    case INSTAGRAM = 'instagram';
    case YOUTUBE = 'youtube';
}

/**
 * @GQL\Input(name="LinkData")
 */
class LinkData
{
    /**
     * @GQL\Field(type="LinkType!")
     */
    public LinkType $type = LinkType::INSTAGRAM;
    public string $url = '';
}

The CompileCommand tried to generate a PHP file that looks like this:

<?php

namespace Project\Generated\__DEFINITIONS__;

use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\Type;
use Overblog\GraphQLBundle\Definition\ConfigProcessor;
use Overblog\GraphQLBundle\Definition\GraphQLServices;
use Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface;
use Overblog\GraphQLBundle\Definition\Type\GeneratedTypeInterface;

/**
 * THIS FILE WAS GENERATED AND SHOULD NOT BE EDITED MANUALLY.
 */
final class LinkDataType extends InputObjectType implements GeneratedTypeInterface, AliasedInterface
{
    public const NAME = 'LinkData';
    
    public function __construct(ConfigProcessor $configProcessor, GraphQLServices $services)
    {
        $config = [
            'name' => self::NAME,
            'fields' => fn() => [
                'type' => [
                    'type' => fn() => Type::nonNull($services->getType('LinkType')),
                    'defaultValue' => \MyApp\LinkType::INSTAGRAM,
                ],
                'url' => [
                    'type' => Type::nonNull(Type::string()),
                    'defaultValue' => '',
                ],
            ],
        ];
        
        parent::__construct($configProcessor->process($config));
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getAliases(): array
    {
        return [self::NAME];
    }
}

however, without the changes in this PR it was failing when generating the line:

'defaultValue' => \MyApp\LinkType::INSTAGRAM,

because \Murtukov\PHPCodeGenerator\Utils::stringifyValue was trying to call __toString method on the enum instance \MyApp\LinkType::INSTAGRAM which is not possible because enums do not have (and cannot have) __toString method.

I realized that when calling stringifyValue with enum instance, we can simply return a fully-qualified reference to the enum value itself. So that is exactly what this PR does.

Note

I was also thinking about returning the string value of the enum, but:

  1. this approach would only work with backed enums and not basic enums.
  2. graphql:dump-schema command was throwing this error:
[GraphQL\Error\SerializationError]                                                                              
Cannot serialize value "instagram" as it must be an instance of enum MyApp\LinkType. 

Exception trace:
  at vendor/overblog/graphql-bundle/src/Definition/Type/PhpEnumType.php:117

@sustmi
Copy link
Author

sustmi commented Nov 13, 2024

OK, I realized that using php-code-generator is pretty simple so here is a much simpler example that does not use overblog/graphql-bundle of a use-case that this PR fixes:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Murtukov\PHPCodeGenerator\Modifier;
use Murtukov\PHPCodeGenerator\PhpFile;

enum LinkType: string
{
    case INSTAGRAM = 'instagram';
    case YOUTUBE = 'youtube';
}

$file = PhpFile::new()->setNamespace('App\Generator');

$file
    ->createClass('LinkData')
    ->addProperty('url', Modifier::PUBLIC, 'string')
    ->addProperty(
        name: 'type',
        type: LinkType::class,
        // Using enum instance as default value causes error without the changes in this pull-request
        defaulValue: LinkType::INSTAGRAM,
    );

echo $file;

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.

1 participant