Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQLite is automatically making interface implementations when it shouldn't #423

Open
MattBred opened this issue Dec 13, 2021 · 10 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@MattBred
Copy link
Contributor

MattBred commented Dec 13, 2021

Version: 5.0.1

According to https://graphqlite.thecodingmachine.io/docs/inheritance-interfaces, GraphQLite should only make an Impl type when it can't find another type that implements that interface:

If GraphQLite cannot find a proper GraphQL Object type implementing an interface, it will create an object type "on the fly".

However, it does it no matter what on my end:

/**
 * @GraphQL\Type
 */
interface MobileAppInterface
{
	/**
	 * @GraphQL\Field 
	 */
	public function getVersionCode(): ?int;
}

/**
 * @GraphQL\Type
 */
class AndroidApp implements MobileAppInterface
{
	public function getVersionCode(): ?int
	{
		// TODO: Implement getVersionCode() method.
	}
}

This generates the following schema:

interface MobileAppInterface {
  versionCode: Int
}

type MobileAppImpl implements MobileAppInterface {
  versionCode: Int
}

type AndroidApp implements MobileAppInterface {
  versionCode: Int
}
@oojacoboo
Copy link
Collaborator

@MattBred I've noticed the same thing. It's a bit harmless, as the interface isn't used, just generated in the schema. But, it is sloppy and confusing.

A PR on this would be awesome :)

@oojacoboo oojacoboo added bug Something isn't working help wanted Extra attention is needed labels Dec 14, 2021
@Lappihuan
Copy link
Contributor

As you can see here:

public function getGeneratedObjectTypeFromInterfaceType(MutableInterfaceType $type): MutableObjectType
{
$typeName = $this->namingStrategy->getConcreteNameFromInterfaceName($type->name);
if ($this->typeRegistry->hasType($typeName)) {
return $this->typeRegistry->getMutableObjectType($typeName);
}
$type = new ObjectFromInterfaceType($typeName, $type);
$type->freeze();
$this->typeRegistry->registerType($type);
return $type;
}

Your type somehow is not yet part of the typeRegistry, might be a ordering issue in the recursive type mapper?

@oojacoboo
Copy link
Collaborator

It's probably related to the mapping cache in the RecursiveTypeMapper. Note that the Interface type names are different. The implementation around this is shaky. It mostly works, but an improved design is really needed.

@Lappihuan
Copy link
Contributor

You are right, this seems quite naive. The interface type name is generated in this function called on line 214 above:

public function getConcreteNameFromInterfaceName(string $name): string
{
return str_replace('Interface', '', $name) . 'Impl';
}

It should actually check if there is a class in php implementing this interface. At least thats what the docs suggest it does but apparently not.

@oojacoboo
Copy link
Collaborator

oojacoboo commented Apr 22, 2022

There are two mappings as well, the actual FQCN and the GQL name. I don't find that these two mappings work well with each other either. IMO, we'd benefit from having a single mapping cache that's more robust.

@Lappihuan
Copy link
Contributor

@oojacoboo what do you mean by two mappings?

@oojacoboo
Copy link
Collaborator

The RecursiveTypeMapper has:

  • mappedClasses
  • classToTypeCache
  • classToInputTypeCache
  • interfaceToClassNameMap

I found it frustrating to work with all of these different maps and think this responsibility would probably be better left to a mapping abstraction.

@aszenz
Copy link
Contributor

aszenz commented Apr 24, 2023

I have ran into the same issue, this causes issues when generating types from schema, i have to handle the interface type even though i already know it would be one of N types

@oojacoboo
Copy link
Collaborator

A PR on this is certainly welcomed!

@joelcollinsdc
Copy link

You are right, this seems quite naive. The interface type name is generated in this function called on line 214 above:

public function getConcreteNameFromInterfaceName(string $name): string
{
return str_replace('Interface', '', $name) . 'Impl';
}

It should actually check if there is a class in php implementing this interface. At least thats what the docs suggest it does but apparently not.

My guess is the code that is commented out here is what is supposed to be skipping this creation if a class implementing that interface exists?

if ($type instanceof MutableInterfaceType /*&& isset($supportedClasses[$closestClassName]) && ! empty($supportedClasses[$closestClassName]->getChildren())*/) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants