9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LighthouseでInterface TypeとUnion Typeを使いこなす【Laravel + GraphQL】

Posted at

LighthouseのInterfaceやUnionを実装する際にちょっとハマったのと、それらに関する情報が調べても全然出てこなかったので記事にしました。

環境

PHP: 7.2.5
Laravel: 7.0
Lighthouse: 4.13

Interface Type

GraphQLのInterfaceはJavaやPHPでいう抽象(abstruct)フィールドと同様に、Interfaceで定義したフィールドを継承したTypeでも提供することを求められます。

またInterfaceを継承したTypeではそれ以外の値は自由に付け足すことが可能です。

GraphQL:Schemas and Types#Interfaces

interface Animal {
    id: ID!
    name: String!
}

type Cat implements Animal {
    id: ID!
    name: String!
    feed: String
}

type Dog implements Animal {
    id: ID!
    name: String!
    favorite: [DogItem!]!
}

type DogItem {
    id: ID!
    name: String
}

Iterface Typeを使用するメリットはそれ自体をQueryやMutationの引数や返り値に指定できることです。

例えば以下のような動物の一覧を返すQueryを定義したとします。

schema.graphql
type Query {
    animals: [Animal!]!
}

これを実行した場合、次のようにCatDogTypeが混ざったレスポンスが得られます。

query
query {
  animals {
    id
    name
    __typename
    
    ... on Cat {
      feed
    }
    
    ... on Dog {
      favorite {
        id
        name
      }
    }
  }
}
response
{
  "data": {
    "animals": [
      {
        "id": "1",
        "name": "hoge_cat",
        "__typename": "Cat",
        "feed": "pet_food"
      },
      {
        "id": "2",
        "name": "fuga_dog",
        "__typename": "Dog",
        "favorite": [
          {
            "id": "1",
            "name": "ball"
          }
        ]
      },
      {
        "id": "3",
        "name": "piyo_cat",
        "__typename": "Cat",
        "feed": "cat_food"
      }
    ]
  }
}

実装上の注意点

Lighthouse:Types #Interface

LighthouseでInterfaceを用いて実装するには通常のディレクティブ(@allや@createなど)を使用することはできず、Resolverを使用する必要があります。

$ php artisan lighthouse:query AnimalResolver
schema.graphql
type Query {
    animals: [Animal!]! @field(resolver: "App\\GraphQL\\Queries\\AnimalResolver@list")
}

またInterfaceを継承するTypeと同名のCatDogのモデルも作成します。
この時、配列や異なる名前のモデルを使用するとbasename() expects parameter 1 to be string, array givenというエラーが発生するので注意しましょう。

$ php artisan make:model Cat
$ php artisan make:model Dog
App\GraphQL\Queries\AnimalResolver
class AnimalResolver
{
    public function list($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $hoge_cat = new Cat;
        $hoge_cat->id = 1;
        $hoge_cat->name = 'hoge_cat';
        $hoge_cat->feed = 'pet_food';

        $fuga_dog = new Dog;
        $fuga_dog->id = 2;
        $fuga_dog->name = 'fuga_dog';

        // ※Interfaceを継承していない通常のTypeは配列でも問題ない。
        $fuga_dog_item = [
                'id' => 1,
                'name' => 'ball'
        ];

        // モデルを使用する場合は以下のようにする。
        // $fuga_dog_item = new DogItem;
        // $fuga_dog_item->id = 1;
        // $fuga_dog_item->name = 'ball';

        $fuga_dog->favorite = [$fuga_dog_item];

        $piyo_cat = new Cat;
        $piyo_cat->id = 3;
        $piyo_cat->name = 'piyo_cat';
        $piyo_cat->feed = 'cat_food';

        return [
            $hoge_cat,
            $fuga_dog,
            $piyo_cat
        ];
    }
}

Union

Union Typeは他のTypeを単に列挙する抽象Typeです。
ただしInterfaceとは異なり、フィールドを定義することはできません。

GraphQL:Schemas and Types#Union types

union Book = Novel | Comic

type Novel {
    novel_title: String!
}

type Comic {
    comic_title: String!
}

Queryの実行方法はInterfaceと同様にonを用いることでそれぞれのTypeのレスポンスを指定することができます。

schema.graphql
type Query {
    books: [Book!]!
}
query
query {
  books {
    __typename
    
    ... on Novel {
      novel_title
    }
    
    ... on Comic {
      comic_title
    }
  }
}
response
{
  "data": {
    "books": [
      {
        "__typename": "Novel",
        "novel_title": "hoge title"
      },
      {
        "__typename": "Comic",
        "comic_title": "comic title"
      }
    ]
  }
}

実装上の注意点

Lighthouse:Types #Union

Interfaceと同様にUnionを用いる場合には通常のディレクティブを使用できないため、Resolverを使用する必要があります。

またレスポンスのTypeに関してもModelを使用しないとエラーとなるので注意しましょう。

App\GraphQL\Queries\BookResolver
class BookResolver
{
    public function list($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $novel = new Novel;
        $novel->novel_title = 'hoge title';

        $comic = new Comic;
        $comic->comic_title = 'fuga title';

        return [
            $novel,
            $comic
        ];
    }
}

参考文献

GraphQL: Schemas and Types
Lighthouse: Types

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?