0Pricing
PHP Academy · Leçon

Résolveurs, mutations et abonnements

Récupérez et modifiez des données grâce aux résolveurs.

Résolveurs, mutations et abonnements est une leçon PHP Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage PHP Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours PHP Academy comprend 4 leçons au total.

Les résolveurs contiennent la logique

Le schéma décrit ce qui existe ; les résolveurs déterminent comment la valeur de chaque champ est produite. Un résolveur est simplement une fonction appelable. Les mutations sont des résolveurs qui modifient l'état. Les souscriptions diffusent des valeurs au fil du temps. Cette leçon couvre ces trois éléments ainsi que le modèle d'exécution qui les relie.

La signature d'un résolveur

Chaque résolveur reçoit quatre arguments : ($objectValue, $args, $context, ResolveInfo $info).

  • $objectValue — la valeur résolue du parent (le rootValue au sommet).
  • $args — les arguments du champ.
  • $context — l'état partagé propre à la requête (connexion à la base de données, utilisateur actuel).
  • $info — les métadonnées AST et du champ (nom du champ, ensemble de sélection, chemin).
<?php
use GraphQL\Type\Definition\ResolveInfo;

$resolve = function ($objectValue, array $args, $context, ResolveInfo $info) {
    // $context['db'], $context['user'] set up per request
    return $context['db']->find($args['id']);
};

Le résolveur par défaut

Si vous ne fournissez pas resolve, le résolveur par défaut de graphql-php lit le nom du champ dans la valeur parente : une clé de tableau, une propriété publique ou une méthode get<Field>(). Vous pouvez ainsi souvent résoudre des types objet entiers sans code répétitif, en renvoyant de simples tableaux ou des objets de transfert de données depuis le parent.

<?php
// Parent returns this array; child fields resolve by key automatically:
$user = [
    'id' => 1,
    'name' => 'Ada',
    'email' => 'ada@example.com',
];
// 'name' field -> $user['name'] with no explicit resolver needed
var_dump($user['name']);

Les résolveurs se propagent du parent vers l'enfant

L'exécution se fait de haut en bas : le résolveur Query.user renvoie un utilisateur, qui devient la valeur $objectValue de User.posts, dont le résultat devient le parent de chaque Post.title. Comprendre cette propagation est essentiel : c'est précisément là qu'apparaît le problème N+1 (traité dans la leçon suivante).

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$userType = new ObjectType([
    'name' => 'User',
    'fields' => fn() => [
        'id'    => Type::id(),
        'name'  => Type::string(),
        'posts' => [
            'type' => Type::listOf(Type::string()),
            // $user is the parent value resolved by Query.user
            'resolve' => fn($user) => Posts::titlesForUser($user['id']),
        ],
    ],
]);

Retourner des promesses (asynchrone)

Les résolveurs peuvent renvoyer une valeur ou une promesse. graphql-php fournit un adaptateur de promesses synchrones ; avec les adaptateurs ReactPHP/Amp, la résolution peut être différée et regroupée. Même en mode synchrone, le renvoi d'objets Deferred permet à l'exécuteur de collecter le travail et de l'exécuter après le niveau de résolution actuel — c'est le mécanisme sur lequel repose DataLoader.

<?php
use GraphQL\Deferred;

$resolve = function ($post) use ($authorBuffer) {
    $authorBuffer->add($post['author_id']);   // queue the id
    return new Deferred(function () use ($authorBuffer, $post) {
        $authorBuffer->loadOnce();             // one batched query
        return $authorBuffer->get($post['author_id']);
    });
};

Les mutations modifient l'état

Une Mutation est simplement un type racine nommé Mutation. Par convention, ses champs de premier niveau s'exécutent séquentiellement (et non en parallèle), afin d'ordonner les effets de bord. Les entrées sont généralement regroupées dans un InputObjectType pour obtenir une signature claire.

<?php
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\Type;

$createPostInput = new InputObjectType([
    'name' => 'CreatePostInput',
    'fields' => [
        'title' => Type::nonNull(Type::string()),
        'body'  => Type::string(),
    ],
]);

Configurer le type de mutation

Le champ de mutation accepte l'objet d'entrée comme argument et renvoie l'entité créée (afin que les clients puissent relire les champs au cours du même aller-retour). Effectuez la validation et l'autorisation dans le résolveur, en lançant une exception en cas d'échec.

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$mutationType = new ObjectType([
    'name' => 'Mutation',
    'fields' => [
        'createPost' => [
            'type' => $postType,
            'args' => ['input' => Type::nonNull($createPostInput)],
            'resolve' => function ($root, array $args, $context) {
                if (!$context['user']) {
                    throw new \RuntimeException('Unauthenticated');
                }
                return PostRepo::create($args['input'], $context['user']);
            },
        ],
    ],
]);

Erreurs : sûres pour le client ou internes

Par défaut, graphql-php masque les messages d'exception et affiche Internal server error afin d'éviter de divulguer des informations internes. Pour exposer un message aux clients, implémentez GraphQL\Error\ClientAware et renvoyez true depuis isClientSafe(). Ajoutez des codes lisibles par machine via extensions.

<?php
use GraphQL\Error\ClientAware;

class ValidationError extends \RuntimeException implements ClientAware {
    public function isClientSafe(): bool { return true; }
    // older versions also used getCategory(): string
}

Les souscriptions : le concept

Un type racine de souscription permet aux clients de recevoir un flux de résultats lorsque des événements se produisent (nouveau message, variation de prix). La spécification GraphQL définit la sémantique des souscriptions, mais graphql-php exécute une seule opération par appel : il ne fait pas fonctionner lui-même un serveur de sockets de longue durée. C'est à vous de fournir le transport.

  • graphql-php résout la charge utile de la souscription pour chaque événement que vous envoyez.
  • Un transport (WebSocket via Ratchet/Mercure/Pusher) distribue les événements aux clients.

Structure d'un résolveur de souscription

En pratique, vous divisez une souscription en une étape de souscription (enregistrer l'intérêt, puis renvoyer une source d'événements) et une étape de résolution (associer chaque événement à la charge utile du champ). De nombreuses piles PHP associent graphql-php à Mercure ou à un courtier de publication/abonnement ; le résolveur ci-dessous montre l'association par événement dont graphql-php est responsable.

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$subscriptionType = new ObjectType([
    'name' => 'Subscription',
    'fields' => [
        'messageAdded' => [
            'type' => $messageType,
            'args' => ['channelId' => Type::nonNull(Type::id())],
            // graphql-php resolves each pushed event into the payload;
            // a WebSocket/Mercure transport drives when this runs.
            'resolve' => fn($event) => $event['message'],
        ],
    ],
]);

Le contexte est votre canal d'authentification et de DI

Le troisième argument du résolveur, $context, est créé une fois par requête et transmis à chaque résolveur. C'est l'endroit approprié pour l'utilisateur authentifié, une connexion à la base de données et vos DataLoaders. Centraliser l'authentification ici permet de garder des résolveurs simples : ils demandent au contexte qui est l'utilisateur au lieu de le déterminer à nouveau.

<?php
require 'vendor/autoload.php';

// Built once per HTTP request, passed to executeQuery():
$context = [
    'user' => authenticate($_SERVER['HTTP_AUTHORIZATION'] ?? ''),
    'db'   => $pdo,
];

$resolve = function ($root, array $args, array $context) {
    if ($context['user'] === null) {
        throw new \RuntimeException('Unauthenticated');
    }
    return $context['db']->find($args['id']);
};

Vérification rapide

Comment rendre un message d'exception visible pour les clients GraphQL ?

Récapitulatif

Vous avez appris le cœur de l'exécution de GraphQL :

  • Les résolveurs acceptent ($value, $args, $context, $info) ; le résolveur par défaut lit les clés et les accesseurs du parent.
  • La résolution se propage du parent vers l'enfant : c'est la source du problème N+1.
  • Le renvoi de Deferred/promesses permet le regroupement.
  • Les mutations sont des champs racine séquentiels utilisant InputObjectType ; effectuez l'autorisation et la validation dans le résolveur.
  • Les souscriptions définissent la résolution de la charge utile, tandis que vous fournissez le transport.
  • ClientAware contrôle les messages d'erreur que les clients peuvent voir.

Ensuite : éliminer le problème N+1 avec DataLoader.

Questions Fréquemment Posées

La leçon « Résolveurs, mutations et abonnements » est-elle gratuite ?

Oui — le texte complet de « Résolveurs, mutations et abonnements » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours PHP Academy, passe à CoddyKit PRO. Le cours PHP Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Résolveurs, mutations et abonnements » ?

Récupérez et modifiez des données grâce aux résolveurs. Tu pratiques PHP Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer PHP Academy ?

Aucune expérience préalable n'est requise. PHP Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Résolveurs, mutations et abonnements » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon PHP Academy ?

Oui. Chaque leçon PHP Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. GraphQL ou REST
  2. Créer un schéma avec graphql-php
  3. Résolveurs, mutations et abonnements
  4. Performances : N+1 et DataLoader
← Retour à PHP Academy