graphql-phpでスキーマを構築する
webonyx/graphql-phpで型とスキーマを定義します。
「graphql-phpでスキーマを構築する」はCoddyKit上の無料PHP Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPHP Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 PHP Academyコースには全4レッスンが含まれています。
graphql-php:リファレンス実装
webonyx/graphql-phpは、事実上標準となっているGraphQLリファレンス実装のPHP移植版です。型システム、パーサー、バリデーター、エグゼキューターを提供します。スキーマはプログラムで(PHPオブジェクトを使って)定義することも、SDLから(スキーマファーストで)定義することもできます。このレッスンでは、各要素の役割を正確に理解できるよう、スキーマを手作業で構築します。
composer require webonyx/graphql-phpスカラーと型レジストリ
GraphQLのすべての値は、最終的にスカラーになります。Int、Float、String、Boolean、IDが該当します。graphql-phpでは、これらはTypeファサード上にあります。オブジェクト型は互いに(場合によっては自分自身も)参照するため、各型を一度だけ構築できるよう、静的なTypeRegistryでメモ化するのが一般的なパターンです。
<?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!"
ObjectTypeを定義する
ObjectTypeにはnameとfieldsマップがあります。各フィールドではtypeを宣言し、必要に応じてresolveコールバックを指定します。resolveを省略すると、graphql-phpはデフォルトリゾルバーを使用し、親の値から対応する配列キー、プロパティ、またはゲッターを読み取ります。
<?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(),
],
]);
Non-Nullとリストによるラップ
ラップ型によって、null許容性と要素数を表現できます。
Type::nonNull(T)→T!(nullにならない)。Type::listOf(T)→[T](リスト。リスト自体がnullになる可能性があり、nullの要素を含む可能性もあります)。[Post!]!= nullでないPostの、nullでないリスト →nonNull(listOf(nonNull($postType)))。
ここは正しく定義してください。これは、クライアントに対するスキーマのnull契約です。
<?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!]!
遅延フィールドで循環参照を解消する
Userにはpostsがあり、Postには(Userである)authorがあります。相互参照する型を構築するには、fieldsを配列ではなくクロージャとして渡します。クロージャは両方の型が存在した後に遅延実行されるため、卵が先か鶏が先かという問題を回避できます。
<?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
],
]);
}
}
フィールド引数
フィールドには引数を渡せます。argsキーの下で宣言すると、リゾルバーの第2引数($args)として渡されます。引数自体にも型があり、Non-Nullにでき、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']})",
];
ルートQuery型
すべてのスキーマにはルートQuery型が必要です。これは、クライアントが処理を開始できるエントリーポイントです。ここでは、最初から最後まで実行できることを確認するため、単一のhelloフィールドを公開します。ルートリゾルバーの第1引数は、スキーマのrootValue(多くの場合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'],
],
],
]);
スキーマの組み立てと実行
Query型をSchemaでラップし、クエリ文字列をGraphQL::executeQuery()に渡して実行します。結果オブジェクトは、toArray()によって標準的な{ data, errors }配列に変換できます。
<?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"}}
BuildSchemaを使うスキーマファーストの方法
SDLを好む場合は、BuildSchema::build()によってスキーマ文字列を解析し、実行可能なスキーマを構築できます。その後、リゾルバー(フィールドリゾルバーのコールバックなど)を別途設定します。これにより、型定義は宣言的に保ちながら、ロジックは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.
リリース前に検証する
graphql-phpは、実行前に受け取ったクエリをスキーマに対して自動的に検証します。また、$schema->assertValid()を使って、構築時やCI実行時にスキーマ自体の整合性を検証することもできます。デプロイ後のリクエスト時ではなく、デプロイ前に誤記や壊れた参照を検出しましょう。
<?php
use GraphQL\Type\Schema;
/** @var Schema $schema */
$schema->assertValid(); // throws InvariantViolation on a broken schema
echo "schema OK\n";
列挙型とカスタムスカラー
オブジェクト以外にも、ほとんどのスキーマを構成する2種類の型があります。EnumTypeは、フィールドの値を固定された名前付きの値の集合に制限します。CustomScalarTypeを使うと、独自のserialize/parseValue/parseLiteralロジックによってドメインスカラー(DateTime、Email)を定義できます。これにより、値を境界で検証し、正規化できます。
<?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.
理解度チェック
fieldsをクロージャとして渡すのはなぜですか?
まとめ
スキーマをゼロから構築しました。
- スカラーとラップ型(
nonNull、listOf)によって、null許容性と要素数の契約を表現します。 - フィールドマップ(または遅延クロージャ)を持つ
ObjectTypeによって、データの形を定義します。 - メモ化する型レジストリによって、循環参照を処理します。
- フィールドには型付きの
argsを渡し、ルートQuery型をエントリーポイントにします。 GraphQL::executeQuery()で実行し、assertValid()でCIにおけるスキーマの整合性を守ります。
次は、リゾルバー、Mutation、Subscriptionです。
よくある質問
「graphql-phpでスキーマを構築する」レッスンは無料ですか?
はい。「graphql-phpでスキーマを構築する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、PHP Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 PHP Academyコースには全4レッスンが含まれています。
「graphql-phpでスキーマを構築する」で何を学びますか?
webonyx/graphql-phpで型とスキーマを定義します。 ブラウザで直接実行するハンズオンコードでPHP Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
PHP Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPHP Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「graphql-phpでスキーマを構築する」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPHP Academyレッスンでコードを書いて実行できますか?
はい。すべてのPHP Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- GraphQLとRESTの比較
- graphql-phpでスキーマを構築する
- リゾルバー、ミューテーション、サブスクリプション
- パフォーマンス:N+1とDataLoader