コンテンツにスキップ

フィールド

GraphQL APIのエントリーポイントは、ルートタイプである Query, Mutation, Subscription のフィールドです。

すべての フィールドには関数が関連付けられており、クエリの一部としてフィールドが要求されたときに呼び出されます。 この関数は resolver と呼ばれます。

Hello World

このセクションでは伝統に従って、Lighthouseで"Hello World!"を出力する方法を学びます。

まず、最もシンプルなスキーマを定義することから始めます。ルートであるQueryタイプにhelloというフィールドを一つ追加しString を返します。

type Query {
  hello: String!
}

これはデータの形式を定義し、クライアントに対してどんなクエリを期待できるかを示します。 次に、実際のリゾルバを実装する必要があります。

デフォルトでは、Lighthouseはフィールド名がの先頭が大文字になっているクラスをApp\GraphQL\QueriesまたはApp\GraphQL\Mutationsから探し、そのクラスの__invoke関数を通常のリゾルバの引数で呼びます。

今回の場合、フィールドはクエリーでhelloという名前なので、以下のようにクラスを定義する必要があります。

<?php

namespace App\GraphQL\Queries;

class Hello
{
    public function __invoke(): string
    {
        return 'world!';
    }
}

このようなクラスを作成する最も簡単な方法は、Lighthouseが用意しているartisanコマンドであるlighthouse:querylighthouse:mutationを使用することです。どちらも引数を1つ取ります。 引数の内容は生成したいフィールドの名前です。

例えば、helloというフィールドのクラスを生成するには、以下のようにします。

php artisan lighthouse:query Hello

これで定義したスキーマのクエリを実行できるようになりました。

{
  hello
}

このクエリは以下のレスポンスを返します:

{
  "data": {
    "hello": "world!"
  }
}

引数を持つフィールド

これまで学んだように、全ての フィールドにはリゾルバ関数が関連付けられています。 関数と同じようにフィールドも引数を取ってその振る舞いを制御することができます。

ユーザーに挨拶するクエリを作ってみましょう。挨拶のメッセージを作成するのに使用する、必須の引数 name を追加します。

type Query {
  greet(name: String!): String
}

このフィールドの最小限の実装は、以下のようになります。 このクラスの雛形はphp artisan lighthouse:query Greetで作成できます。

リゾルバ関数の第2引数は、クエリに渡される引数の連想配列です。

<?php

namespace App\GraphQL\Queries;

class Greet
{
    public function __invoke($rootValue, array $args): string
    {
        return "Hello, {$args['name']}!";
    }
}

このクエリを任意のnameを渡して呼び出すことができます。

{
  greet(name: "Foo")
}

すると、よりフレンドリーな挨拶メッセージが受け取れます。

{
  "data": {
    "greet": "Hello, Foo!"
  }
}

もしユーザーに引数を渡したくない場合は、スキーマを修正してnameを任意の引数にし、デフォルト値を指定します。

type Query {
  greet(name: String = "you"): String
}

これによって、以下のようにクエリを使用できます。

{
  greet
}
{
  "data": {
    "greet": "Hello, you!"
  }
}

非ルートなフィールドのリゾルバ

前述のとおり、スキーマのすべてのフィールドにはリゾルバがあります。 しかし、ルート型に属さないフィールドはどうでしょうか?

type Query {
  user: User!
}

type User {
  id: ID!
  name: String!
  email: String
}

さて、クライアントが以下のクエリを送信したときに何が起こるかを見てみましょう:

{
  user {
    id
    name
  }
}

まず、userのリゾルバが呼び出されます。ここでは、AppModelUserのインスタンスすると仮定します。

次に、フィールドのサブセレクションが解決されます。ここで、リクエストされたフィールドはidnameの2つです。 親フィールドであるUserはすでに解決しているので、その属性を取得するために再度取得する必要はありません。

便利なことに、各リゾルバの最初の引数は親のフィールドが渡されます。この場合はUserモデルです。

'id`に対するリゾルバのシンプルな実装は、以下のようになります。

<?php

use App\Models\User;

function resolveUserId(User $user): string
{
    return $user->id;
}

このようなリゾルバをひとつひとつ書いていくのは、かなり冗長になります。 4番目のリゾルバの引数であるResolveInfoを使用することで、リクエストされたフィールド名にアクセスし、一致するプロパティに動的にアクセスできます。

<?php

use App\Models\User;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

function resolveUserAttribute(User $user, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
{
    return $user->{$resolveInfo->fieldName};
}

幸いなことに、基礎となるGraphQL実装はすでに賢明なデフォルトリゾルバを提供しており、Eloquentモデルや連想配列といったルートリゾルバから返されるデータとうまく連携しています。

これが意味するところは、ほとんどの場合においてはルートフィールド用のリゾルバを用意し、それが適切な形のデータを返すようにするだけでよいということです。

もし、ルートタイプQueryMutation以外のフィールドにカスタムリゾルバを実装する必要がある場合には、@field、または@methodディレクティブを使用できます。

必要であればデフォルトのリゾルバの変更も可能です。