0Pricing
PHP Academy · Lektion

Resolver, Mutationen und Subscriptions

Daten mithilfe von Resolvern abrufen und ändern

Resolver, Mutationen und Subscriptions ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Resolver enthalten die Logik

Das Schema beschreibt was existiert; Resolver legen fest, wie der Wert jedes Feldes erzeugt wird. Ein Resolver ist einfach ein Callable. Mutations sind Resolver, die den Zustand ändern. Subscriptions streamen Werte über die Zeit. Diese Lektion behandelt alle drei sowie das Ausführungsmodell, das sie miteinander verbindet.

Die Resolver-Signatur

Jeder Resolver erhält vier Argumente: ($objectValue, $args, $context, ResolveInfo $info).

  • $objectValue — der aufgelöste Wert des Parents (oben der rootValue).
  • $args — die Argumente des Feldes.
  • $context — der gemeinsame Zustand pro Anfrage (DB-Handle, aktueller Benutzer).
  • $info — AST-/Feldmetadaten (Feldname, Selection Set, Pfad).
<?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']);
};

Der Standard-Resolver

Wenn Sie kein resolve angeben, liest der Standard-Resolver von graphql-php den Feldnamen aus dem Parent-Wert: als Array-Schlüssel, öffentliche Eigenschaft oder über eine get<Field>()-Methode. Dadurch können Sie ganze Objekttypen oft ohne Boilerplate auflösen, indem Sie aus dem Parent einfach Arrays oder DTOs zurückgeben.

<?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']);

Resolver werden vom Parent zum Child verkettet

Die Ausführung erfolgt von oben nach unten: Der Resolver von Query.user gibt einen Benutzer zurück, der zum $objectValue für User.posts wird; dessen Ergebnis wird zum Parent für jedes Post.title. Dieses Verständnis der Kaskade ist unverzichtbar — genau hier tritt das N+1-Problem auf (in der nächsten Lektion behandelt).

<?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']),
        ],
    ],
]);

Promises zurückgeben (asynchron)

Resolver dürfen einen Wert oder ein Promise zurückgeben. graphql-php wird mit einem synchronen Promise-Adapter ausgeliefert; mit ReactPHP-/Amp-Adaptern kann die Auflösung verzögert und gebündelt werden. Auch bei synchroner Ausführung können Sie Deferred-Objekte zurückgeben. Dadurch sammelt der Executor Arbeit und führt sie nach der aktuellen Auflösungsebene aus — darauf basiert 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']);
    });
};

Mutations ändern den Zustand

Eine Mutation ist einfach ein Root-Typ namens Mutation. Konventionsgemäß werden seine obersten Felder nacheinander (nicht parallel) ausgeführt, damit Seiteneffekte geordnet bleiben. Eingaben werden für eine klare Signatur typischerweise in einem InputObjectType gebündelt.

<?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(),
    ],
]);

Den Mutation-Typ einbinden

Das Mutation-Feld nimmt das Input-Objekt als Argument entgegen und gibt die erstellte Entität zurück (damit Clients im selben Roundtrip wieder Felder lesen können). Führen Sie Validierung und Autorisierung im Resolver durch und lösen Sie bei Fehlern eine Exception aus.

<?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']);
            },
        ],
    ],
]);

Fehler: Für Clients sichere vs. interne

Standardmäßig verbirgt graphql-php Exception-Meldungen und zeigt Internal server error an, um Interna nicht preiszugeben. Damit Clients eine Meldung erhalten, implementieren Sie GraphQL\Error\ClientAware und geben aus isClientSafe() true zurück. Fügen Sie über extensions maschinenlesbare Codes hinzu.

<?php
use GraphQL\Error\ClientAware;

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

Subscriptions: Das Konzept

Ein Root-Typ Subscription ermöglicht Clients, bei eintretenden Ereignissen (neue Nachricht, Preis-Tick) einen Ergebnis-Stream zu empfangen. Die GraphQL-Spezifikation definiert die Semantik von Subscriptions, aber graphql-php führt pro Aufruf nur eine Operation aus — es betreibt selbst keinen langlebigen Socket-Server. Den Transport stellen Sie bereit.

  • graphql-php löst für jedes von Ihnen ausgelieferte Ereignis das Payload der Subscription auf.
  • Ein Transport (WebSocket über Ratchet/Mercure/Pusher) liefert Ereignisse an Clients aus.

Aufbau eines Subscription-Resolvers

In der Praxis teilen Sie eine Subscription in einen subscribe-Schritt (Interesse registrieren, gibt eine Ereignisquelle zurück) und einen resolve-Schritt (jedes Ereignis auf die Payload des Feldes abbilden). Viele PHP-Stacks kombinieren graphql-php mit Mercure oder einem Pub/Sub-Broker; der folgende Resolver zeigt die Zuordnung pro Ereignis, für die graphql-php zuständig ist.

<?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'],
        ],
    ],
]);

Der Context ist Ihr Kanal für Auth und DI

Das dritte Resolver-Argument, $context, wird einmal pro Anfrage erstellt und an jeden Resolver weitergereicht. Es ist der richtige Ort für den authentifizierten Benutzer, eine Datenbankverbindung und Ihre DataLoader. Wenn Sie die Authentifizierung hier zentralisieren, bleiben Resolver schlank — sie fragen den Context, wer der Benutzer ist, statt dies erneut herzuleiten.

<?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']);
};

Wissenscheck

Wie machen Sie eine Exception-Meldung für GraphQL-Clients sichtbar?

Zusammenfassung

Sie haben den Kern der GraphQL-Ausführung kennengelernt:

  • Resolver nehmen ($value, $args, $context, $info) entgegen; der Standard-Resolver liest Schlüssel und Getter aus dem Parent.
  • Die Auflösung kaskadiert vom Parent zum Child — die Quelle des N+1-Problems.
  • Das Zurückgeben von Deferred/Promises ermöglicht die Bündelung.
  • Mutations sind sequenzielle Root-Felder mit InputObjectType; führen Sie Authentifizierung und Validierung im Resolver durch.
  • Subscriptions definieren die Auflösung der Payload, während Sie den Transport bereitstellen.
  • ClientAware steuert, welche Fehlermeldungen Clients sehen können.

Als Nächstes: das N+1-Problem mit DataLoader beseitigen.

Häufig gestellte Fragen

Ist die Lektion „Resolver, Mutationen und Subscriptions“ kostenlos?

Ja — der vollständige Text von „Resolver, Mutationen und Subscriptions“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Resolver, Mutationen und Subscriptions“?

Daten mithilfe von Resolvern abrufen und ändern Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um PHP Academy zu starten?

Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Resolver, Mutationen und Subscriptions“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?

Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. GraphQL vs. REST
  2. Ein Schema mit graphql-php erstellen
  3. Resolver, Mutationen und Subscriptions
  4. Performance: N+1 und DataLoader
← Zurück zu PHP Academy