PHP Academy · Lektion

Bygga ett schema med graphql-php

Definiera typer och ett schema med webonyx/graphql-php.

Lektion 2 av 413 steg

Bygga ett schema med graphql-php är en gratis lektion i PHP Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för PHP Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i PHP Academy innehåller totalt 4 lektioner.

graphql-php: referensimplementationen

webonyx/graphql-php är de facto-portningen av GraphQL:s referensimplementation till PHP. Den tillhandahåller typsystemet, parsern, validatorn och exekveraren. Schemat kan definieras antingen programmässigt (PHP-objekt) eller från SDL (schema-first). I den här lektionen bygger vi ett schema för hand så att ni förstår exakt vad varje del gör.

composer require webonyx/graphql-php

Skalärer och typregistret

Alla GraphQL-värden utgår i slutänden från en skalär: Int, Float, String, Boolean, ID. I graphql-php finns dessa på Type-fasaden. Eftersom objekttyper refererar till varandra (och till sig själva) är ett vanligt mönster ett statiskt TypeRegistry som memorerar varje typ, så att den bara byggs en gång.

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

// Built-in scalars, returned as singletons:
var_dump(Type::int()->name);     // "Int"
var_dump(Type::string()->name);  // "String"
var_dump(Type::id()->name);      // "ID"
var_dump(Type::nonNull(Type::string())->toString()); // "String!"

Definiera en ObjectType

En ObjectType har ett name och en fields-map. Varje fält deklarerar sin type och kan dessutom ha en resolve-callback. Om resolve utelämnas använder graphql-php standardresolvern, som läser motsvarande arraynyckel eller egenskap/getter från det överordnade värdet.

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

$postType = new ObjectType([
    'name' => 'Post',
    'fields' => [
        'id'    => Type::nonNull(Type::id()),
        'title' => Type::nonNull(Type::string()),
        'body'  => Type::string(),
    ],
]);

Omslag av Non-Null och listor

Omslagstyper uttrycker nullbarhet och kardinalitet:

  • Type::nonNull(T) → T! (aldrig null).
  • Type::listOf(T) → [T] (en lista som kan vara null och även kan innehålla null-element).
  • [Post!]! = en lista som inte är null och som bara innehåller poster som inte är null → nonNull(listOf(nonNull($postType))).

Detta måste bli rätt: det utgör schemats null-kontrakt gentemot klienterna.

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

// [Post!]!  -- a required list whose elements are never null
$wrapped = Type::nonNull(Type::listOf(Type::nonNull(Type::string())));
echo $wrapped->toString(), "\n"; // [String!]!

Lata fält bryter cirkulära referenser

En User har posts, och en Post har en author (en User). För att bygga ömsesidigt refererande typer skickar ni in fields som en closure i stället för som en array. Closure:n körs lazily efter att båda typerna finns, vilket löser hönan-och-ägget-problemet.

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

class Types {
    private static array $cache = [];

    public static function user(): ObjectType {
        return self::$cache['User'] ??= new ObjectType([
            'name' => 'User',
            'fields' => fn() => [           // lazy!
                'id'    => Type::nonNull(Type::id()),
                'name'  => Type::nonNull(Type::string()),
                'posts' => Type::listOf(self::post()),
            ],
        ]);
    }

    public static function post(): ObjectType {
        return self::$cache['Post'] ??= new ObjectType([
            'name' => 'Post',
            'fields' => fn() => [
                'id'     => Type::nonNull(Type::id()),
                'title'  => Type::nonNull(Type::string()),
                'author' => self::user(),    // back-reference
            ],
        ]);
    }
}

Fältargument

Fält kan ta emot argument. Deklarera dem under nyckeln args; de skickas in som den andra parametern ($args) till resolvern. Argumenten är själva typade, kan vara non-null och kan ha defaultValue.

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

$userField = [
    'type' => Type::string(),
    'args' => [
        'id'     => Type::nonNull(Type::id()),
        'locale' => ['type' => Type::string(), 'defaultValue' => 'en'],
    ],
    'resolve' => fn($root, array $args) => "user {$args['id']} ({$args['locale']})",
];

Rottypen Query

Varje schema behöver en rot-Query-typ — de startpunkter som klienterna kan utgå från. Här exponerar vi ett enda hello-fält så att vi kan köra allt från början till slut. Rotresolverns första argument är schemats rootValue (ofta null).

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

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        'hello' => [
            'type' => Type::string(),
            'args' => ['name' => Type::nonNull(Type::string())],
            'resolve' => fn($root, array $args) => 'Hello, ' . $args['name'],
        ],
    ],
]);

Sätt ihop och kör schemat

Omslut Query-typen i en Schema och kör en frågesträng genom GraphQL::executeQuery(). Resultatobjektet omvandlas till den kanoniska { data, errors }-arrayen via toArray().

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

use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        'hello' => [
            'type' => Type::string(),
            'args' => ['name' => Type::nonNull(Type::string())],
            'resolve' => fn($root, $args) => 'Hello, ' . $args['name'],
        ],
    ],
]);

$schema = new Schema(['query' => $queryType]);
$result = GraphQL::executeQuery($schema, '{ hello(name: "Ada") }');
echo json_encode($result->toArray());
// {"data":{"hello":"Hello, Ada"}}

Schema-first-alternativ med BuildSchema

Om ni föredrar SDL kan BuildSchema::build() tolka en schemasträng till ett körbart schema. Därefter kopplar ni in resolvers separat (till exempel via en field-resolver-callback), vilket håller typdefinitionerna deklarativa medan logiken förblir i PHP.

<?php
use GraphQL\Utils\BuildSchema;

$sdl = <<<'GQL'
type Query {
  hello(name: String!): String
}
GQL;

$schema = BuildSchema::build($sdl);
// Provide resolvers via the executeQuery $fieldResolver argument
// or with a type config decorator.

Validera före driftsättning

graphql-php validerar automatiskt inkommande frågor mot schemat före körningen. Ni kan även kontrollera att schemat självt är internt konsekvent vid byggtid eller i CI med $schema->assertValid() — upptäck stavfel och trasiga referenser före driftsättning, inte när en begäran hanteras.

<?php
use GraphQL\Type\Schema;

/** @var Schema $schema */
$schema->assertValid(); // throws InvariantViolation on a broken schema
echo "schema OK\n";

Enumerationer och anpassade skalärer

Utöver objekt kompletterar två typvarianter de flesta scheman. EnumType begränsar ett fält till en fast uppsättning namngivna värden. CustomScalarType låter er definiera domänskalärer (DateTime, Email) med egen logik för serialize/parseValue/parseLiteral, så att värden valideras och normaliseras vid systemgränsen.

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

$statusEnum = new EnumType([
    'name' => 'PostStatus',
    'values' => [
        'DRAFT'     => ['value' => 0],
        'PUBLISHED' => ['value' => 1],
        'ARCHIVED'  => ['value' => 2],
    ],
]);
// A field typed as $statusEnum only accepts DRAFT/PUBLISHED/ARCHIVED.

Snabbtest

Varför skickar man in fields som en closure?

Sammanfattning

Ni byggde ett schema från grunden:

  • Skalärer och omslagstyper (nonNull, listOf) uttrycker kontraktet för nullbarhet och kardinalitet.
  • ObjectType med en fältmap eller en lat closure definierar era strukturer.
  • Ett memorerande typregister hanterar cirkulära referenser.
  • Fält tar typade args; rot-typen Query är startpunkten.
  • GraphQL::executeQuery() kör schemat; assertValid() skyddar schemat i CI.

Nästa steg: resolvers, mutationer och prenumerationer.

Gratis att börja

Lär dig PHP med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
49
Lektioner
195

Vanliga frågor

Är lektionen ”Bygga ett schema med graphql-php” gratis?

Ja – hela texten till ”Bygga ett schema med graphql-php” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i PHP Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i PHP Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Bygga ett schema med graphql-php”?

Definiera typer och ett schema med webonyx/graphql-php. Ni övar på PHP Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig PHP Academy?

Du behöver inga förkunskaper. Utbildningen i PHP Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Bygga ett schema med graphql-php”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här PHP Academy-lektionen?

Ja. Varje PHP Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. GraphQL kontra REST
  2. Bygga ett schema med graphql-php
  3. Resolvers, mutationer och prenumerationer
  4. Prestanda: N+1 och DataLoader
← Tillbaka till PHP Academy