GraphQL avec Symfony

GraphQL avec Symfony


Création du type Query

Avant de mettre en place les resolvers pour les query en lecture. Vous devez créer le type query.

Il faut ensuite créer un type avec l'ensemble des fonctions que vous souhaitez avoir. Nous allons :

  • récupérer l'ensemble des astronautes ;
  • récupérer un astronaute ;
  • récupérer une planète.

Commençons par créer le fichier Query.yaml dans le dossier config/graphql/types.

Dans ce fichier nous allons identifier les points d'entrée du graphql :

Query: type: object config: fields: Astronaut: type: 'Astronaut' args: id: description: 'Resolves Astronaut using its id.' type: 'Int!' Astronauts: type: '[Astronaut]' Planet: type: 'Planet'

Si tout est ok, vous devez avoir la documentation qui s'affiche dans l'interface GraphiQL

Création des resolvers

Si vous essayez la query :

{ Astronauts { id } }

Vous devriez voir la réponse suivante :

{ "data": { "Astronauts": null } }

Puisque pour l'instant vous n'avez aucun resolver.

Le resolver est le code qui permet de récuperer la donnée dans le base.

Dans le bundle il s'agit de service symfony.

Il existe deux façons de créer un resolver :

  • en utilisant des services implémentant les interfaces ResolverInterface, AliasedInterface ;
  • en créant ses propres services.

On va commencer par créer les trois resolver via les interfaces.

Dans le fichier Query.yaml vous devez ajouter les appels aux différents resolver :

Query: type: object config: fields: Astronaut: type: 'Astronaut' args: id: description: 'Resolves Astronaut using its id.' type: 'Int!' resolve: "@=resolver('Astronaut', [args['id']])" Astronauts: type: '[Astronaut]' resolve: "@=resolver('Astronauts')" Planet: type: 'Planet' args: id: description: 'Resolves Planet using its id.' type: 'Int!' resolve: "@=resolver('Planet', [args['id']])"

Puis nous allons créer les services. Créez le dossier src/Resolver.

Ajoutez le fichier PlanetResolver.php avec :

<?php namespace App\Resolver; use App\Repository\PlanetRepository; use Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface; use Overblog\GraphQLBundle\Definition\Resolver\ResolverInterface; final class PlanetResolver implements ResolverInterface, AliasedInterface { /** * @var PlanetRepository */ private $planetRepository; /** * * @param PlanetRepository $planetRepository */ public function __construct(PlanetRepository $planetRepository) { $this->planetRepository = $planetRepository; } /** * @return \App\Entity\Planet */ public function resolve(int $id) { return $this->planetRepository->find($id); } /** * {@inheritdoc} */ public static function getAliases(): array { return [ 'resolve' => 'Planet', ]; } }

Ajoutez le fichier AstronautResolver.php avec :

<?php namespace App\Resolver; use App\Repository\AstronautRepository; use Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface; use Overblog\GraphQLBundle\Definition\Resolver\ResolverInterface; final class AstronautResolver implements ResolverInterface, AliasedInterface { /** * @var AstronautRepository */ private $astronautRepository; /** * * @param AstronautRepository $astronautRepository */ public function __construct(AstronautRepository $astronautRepository) { $this->astronautRepository = $astronautRepository; } /** * @return \App\Entity\Planet */ public function resolve(int $id) { return $this->astronautRepository->find($id); } /** * {@inheritdoc} */ public static function getAliases(): array { return [ 'resolve' => 'Astronaut', ]; } }

Ajoutez le fichier AstronautsResolver.php avec :

<?php namespace App\Resolver; use App\Repository\AstronautRepository; use Overblog\GraphQLBundle\Definition\Resolver\AliasedInterface; use Overblog\GraphQLBundle\Definition\Resolver\ResolverInterface; final class AstronautsResolver implements ResolverInterface, AliasedInterface { /** * @var AstronautRepository */ private $astronautRepository; /** * * @param AstronautRepository $astronautRepository */ public function __construct(AstronautRepository $astronautRepository) { $this->astronautRepository = $astronautRepository; } /** * @return \App\Entity\Astronaut */ public function resolve() { return $this->astronautRepository->findAll(); } /** * {@inheritdoc} */ public static function getAliases(): array { return [ 'resolve' => 'Astronauts', ]; } }

Si tout est ok la réponse à votre requête est :

{ "data": { "Astronauts": [] } }

Et si vous ajoutez des astronautes dans votre base de donnnées et changez la requête en :

{ Astronauts { id, pseudo, grade { id, name } planet { id, name } } }

La réponse devrait être :

{ "data": { "Astronauts": [ { "id": 1, "pseudo": "captainjojo", "grade": { "id": 1, "name": "admiral" }, "planet": null }, { "id": 2, "pseudo": "pouzor", "grade": { "id": 2, "name": "rookie" }, "planet": null }, { "id": 3, "pseudo": "francki", "grade": { "id": 2, "name": "rookie" }, "planet": null } ] } }

Il manque le lien avec la planète, car dans un objet Astronaut nous n'avons pas directement le lien avec la planet.

Nous allons donc mettre en place un autre resolver. Cette fois-ci via un service.

Dans le fichier Astronaut.yaml nous allons ajouter le resolver pour la planète :

Astronaut: type: object config: fields: id: type: 'Int!' pseudo: type: 'String!' grade: type: 'Grade' planet: type: 'Planet' resolve: "@=service('planet.resolver').resolveInAstronaut(value, args, context, info)"

Puis dans le PlanetResolver.php vous pouvez ajouter la fonction de resolve suivante :

public function resolveInAstronaut(Astronaut $astronaut, $args, $context, $info) { return $this->planetRepository->findByAstronaut($astronaut->getId()); }

Vous devez ajouter la fonction suivante dans le PlanetRepository.php :

public function findByAstronaut($id) { return $this->createQueryBuilder('p') ->innerJoin('p.astronauts', 'a') ->andWhere('a.id = :id') ->setParameter('id', $id) ->getQuery() ->getOneOrNullResult(); }

Si tout est bon le résultat de votre précédente requête doit être :

{ "data": { "Astronauts": [ { "id": 1, "pseudo": "captainjojo", "grade": { "id": 1, "name": "admiral" }, "planet": { "id": 1, "name": "duck" } }, { "id": 2, "pseudo": "pouzor", "grade": { "id": 2, "name": "rookie" }, "planet": { "id": 2, "name": "panda" } }, { "id": 3, "pseudo": "francki", "grade": { "id": 2, "name": "rookie" }, "planet": { "id": 2, "name": "panda" } } ] } }

Retrouvez le code directement ici

Auteur(s)

Vincent Composieux

Vincent Composieux

Architecte passionné par les technologies web depuis de longues années, je pratique principalement du PHP (Symfony) / Javascript mais aussi du Python ou Golang.

Voir le profil

Vous souhaitez en savoir plus sur le sujet ?
Organisons un échange !

Notre équipe d'experts répond à toutes vos questions.

Nous contacter

Découvrez nos autres contenus dans le même thème

Astronaute revenant de mission

Retour sur le Forum PHP 2024

Découvrez un résumé concis des conférences qui nous ont le plus marqué lors du Forum PHP 2024 !