When using attributes or annotations, as we use classes to describe our GraphQL objects, it is also possible to create and populate classes instances using GraphQL data.
If a class is used to describe a GraphQL Input, this same class can be instantiated to hold the corresponding GraphQL Input data.
This is where the Arguments Transformer
comes into play. Knowing the matching between GraphQL types and PHP classes, the service is able to instantiate a PHP classes and populate it with data based on the corresponding GraphQL type.
To invoke the Arguments Transformer, we use the input
expression function in our resolvers.
The arguments
function take two parameters, a mapping of arguments name and their type, like name => type
. The type is in GraphQL notation, eventually with the "[]" and "!". The data are indexed by argument name.
This function will use the Arguments Transformer
service to transform the list of arguments into their corresponding PHP class if it has one and using a property accessor, it will populate the instance, and will use the validator
service to validate it.
The transformation is done recursively. If an Input include another Input as field, it will also be populated the same way.
For example:
namespace App\GraphQL\Input;
use Overblog\GraphQLBundle\Annotation as GQL;
use Symfony\Component\Validator\Constraints as Assert;
#[GQL\Input]
class UserRegisterInput {
#[Assert\NotBlank]
#[Assert\Length(min: 2, max: 50)]
#[GQL\Field(type: "String!")]
public $username;
#[Assert\NotBlank]
#[Assert\Email]
#[GQL\Field(type: "String!")]
public $email;
#[Assert\NotBlank]
#[Assert\Length(min: 5, minMessage: "The password must be at least 5 characters long.")]
#[GQL\Field(type: "String!")]
public $password;
#[Assert\NotBlank]
#[Assert\GreaterThan(18)]
#[GQL\Field(type: "Int!")]
public $age;
}
....
#[GQL\Provider]
class UserRepository {
#[GQL\Mutation]
public function createUser(UserRegisterInput $input) : User {
// Use the validated $input here
$user = new User();
$user->setUsername($input->username);
$user->setPassword($input->password);
...
}
}
When this Input is used in a mutation, the Symfony service overblog_graphql.arguments_transformer
is called in order to transform the received array of data into a UserRegisterInput
instance using a property accessor.
Then the validator
service is used to validate this instance against the configured constraints.
The mutation received the valid instance.
In the above example, everything is auto-guessed and a Provider is used. But this would be the same as :
#[GQL\Type]
class RootMutation {
#[GQL\Field(type: "User", resolve: "@=call(service('UserRepository').createUser, arguments({input: 'UserRegisterInput'}, arg))")]
#[GQL\Arg(name: "input", type: "UserRegisterInput")]
public $createUser;
}
So, the resolver (the createUser
method) will receive an instance of the class UserRegisterInput
instead of an array of data.