使用Lighthouse轻松驾驭Laravel + GraphQL的接口类型和联合类型

因为在实现Lighthouse的Interface和Union时遇到了一些困难,而且对于这些问题的相关信息进行了调查但却找不到任何结果,所以我写了一篇文章。

环境

PHP版本为7.2.5
Laravel版本为7.0
Lighthouse版本为4.13

界面类型

GraphQL 中的接口与 Java 或 PHP 中的抽象字段类似,要求接口定义的字段也必须在继承该接口的类型中提供。

在继承了Interface的Type中,可以自由添加除此之外的其他值。

GraphQL: 模式和类型#接口

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
}

使用Interface Type的好处是可以将它本身指定为查询或变更的参数或返回值。

假设我们定义了一个查询,可以返回以下动物的列表。

type Query {
    animals: [Animal!]!
}

如果执行此操作,将获得以下Cat和DogType混合的响应。

query {
  animals {
    id
    name
    __typename

    ... on Cat {
      feed
    }

    ... on Dog {
      favorite {
        id
        name
      }
    }
  }
}
{
  "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实现接口时,通常不能使用常规的指令(例如@all或@create),而需要使用解析器来实现。

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

同名的Cat和Dog模型也会创建,它们都继承自Interface。
此时,如果使用数组或不同名称的模型,将会触发错误:basename()期望第一个参数为字符串,但给出的是数组,所以请注意。

$ php artisan make:model Cat
$ php artisan make:model Dog
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 Type是一种抽象类型,其简单地列举了其他类型。然而,与接口不同的是,它不能定义字段。

GraphQL:模式和类型#联合类型

union Book = Novel | Comic

type Novel {
    novel_title: String!
}

type Comic {
    comic_title: String!
}

在处理查询时,可以通过使用”on”关键字来指定每种类型的响应,就像处理接口一样。

type Query {
    books: [Book!]!
}
query {
  books {
    __typename

    ... on Novel {
      novel_title
    }

    ... on Comic {
      comic_title
    }
  }
}
{
  "data": {
    "books": [
      {
        "__typename": "Novel",
        "novel_title": "hoge title"
      },
      {
        "__typename": "Comic",
        "comic_title": "comic title"
      }
    ]
  }
}

在实施过程中的注意事项

灯塔:类型 #联盟

使用Union时,无法使用常规的指令,因此需要使用Resolver,就像Interface一样。

同样需要注意的是,如果不使用Model对象处理响应类型,将会产生错误。

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: 模式和类型
Lighthouse: 类型

广告
将在 10 秒后关闭
bannerAds