Skip to main content
Version: 4.0

Internals

Mapping types

The core of GraphQLite is its ability to map PHP types to GraphQL types. This mapping is performed by a series of "type mappers".

GraphQLite contains 4 categories of type mappers:

  • Parameter mappers
  • Root type mappers
  • Recursive (class) type mappers
  • (class) type mappers
graph TD; classDef custom fill:#cfc,stroke:#7a7,stroke-width:2px,stroke-dasharray: 5, 5; subgraph RootTypeMapperInterface NullableTypeMapperAdapter-->CompoundTypeMapper CompoundTypeMapper-->IteratorTypeMapper IteratorTypeMapper-->YourCustomRootTypeMapper YourCustomRootTypeMapper-->MyCLabsEnumTypeMapper MyCLabsEnumTypeMapper-->BaseTypeMapper BaseTypeMapper-->FinalRootTypeMapper end subgraph RecursiveTypeMapperInterface BaseTypeMapper-->RecursiveTypeMapper end subgraph TypeMapperInterface RecursiveTypeMapper-->YourCustomTypeMapper YourCustomTypeMapper-->PorpaginasTypeMapper PorpaginasTypeMapper-->GlobTypeMapper end class YourCustomRootTypeMapper,YourCustomTypeMapper custom;

Root type mappers

(Classes implementing the RootTypeMapperInterface)

These type mappers are the first type mappers called.

They are responsible for:

  • mapping scalar types (for instance mapping the "int" PHP type to GraphQL Integer type)
  • detecting nullable/non-nullable types (for instance interpreting "?int" or "int|null")
  • mapping list types (mapping a PHP array to a GraphQL list)
  • mapping union types
  • mapping enums

Root type mappers have access to the context of a type: they can access the PHP DocBlock and read annotations. If you want to write a custom type mapper that needs access to annotations, it needs to be a "root type mapper".

GraphQLite provides 6 classes implementing RootTypeMapperInterface:

  • NullableTypeMapperAdapter: a type mapper in charge of making GraphQL types non-nullable if the PHP type is non-nullable
  • CompoundTypeMapper: a type mapper in charge of union types
  • IteratorTypeMapper: a type mapper in charge of iterable types (for instance: MyIterator|User[])
  • MyCLabsEnumTypeMapper: maps MyCLabs/enum types to GraphQL enum types
  • BaseTypeMapper: maps scalar types and lists. Passes the control to the "recursive type mappers" if an object is encountered.
  • FinalRootTypeMapper: the last type mapper of the chain, used to throw error if no other type mapper managed to handle the type.

Type mappers are organized in a chain; each type-mapper is responsible for calling the next type mapper.

graph TD; classDef custom fill:#cfc,stroke:#7a7,stroke-width:2px,stroke-dasharray: 5, 5; subgraph RootTypeMapperInterface NullableTypeMapperAdapter-->CompoundTypeMapper CompoundTypeMapper-->IteratorTypeMapper IteratorTypeMapper-->YourCustomRootTypeMapper YourCustomRootTypeMapper-->MyCLabsEnumTypeMapper MyCLabsEnumTypeMapper-->BaseTypeMapper BaseTypeMapper-->FinalRootTypeMapper end class YourCustomRootTypeMapper custom;

Class type mappers

(Classes implementing the TypeMapperInterface)

Class type mappers are mapping PHP classes to GraphQL object types.

GraphQLite provide 3 default implementations:

  • CompositeTypeMapper: a type mapper that delegates mapping to other type mappers using the Composite Design Pattern.
  • GlobTypeMapper: scans classes in a directory for the @Type or @ExtendType annotation and maps those to GraphQL types
  • PorpaginasTypeMapper: maps and class implementing the Porpaginas Result interface to a special paginated type.

Registering a type mapper in Symfony

If you are using the GraphQLite Symfony bundle, you can register a type mapper by tagging the service with the "graphql.type_mapper" tag.

Registering a type mapper using the SchemaFactory

If you are using the SchemaFactory to bootstrap GraphQLite, you can register a type mapper using the SchemaFactory::addTypeMapper method.

Recursive type mappers

(Classes implementing the RecursiveTypeMapperInterface)

There is only one implementation of the RecursiveTypeMapperInterface: the RecursiveTypeMapper.

Standard "class type mappers" are mapping a given PHP class to a GraphQL type. But they do not handle class hierarchies. This is the role of the "recursive type mapper".

Imagine that class "B" extends class "A" and class "A" maps to GraphQL type "AType".

Since "B" is a "A", the "recursive type mapper" role is to make sure that "B" will also map to GraphQL type "AType".

Parameter mapper middlewares

"Parameter middlewares" are used to decide what argument should be injected into a parameter.

Let's have a look at a simple query:

/**
* @Query
* @return Product[]
*/
public function products(ResolveInfo $info): array

As you may know, the ResolveInfo object injected in this query comes from Webonyx/GraphQL-PHP library. GraphQLite knows that is must inject a ResolveInfo instance because it comes with a ResolveInfoParameterHandler class that implements the ParameterMiddlewareInterface).

You can register your own parameter middlewares using the SchemaFactory::addParameterMiddleware() method, or by tagging the service as "graphql.parameter_middleware" if you are using the Symfony bundle.

Use a parameter middleware if you want to inject an argument in a method and if this argument is not a GraphQL input type or if you want to alter the way input types are imported (for instance if you want to add a validation step)