在 GraphQL 中查询未知数据结构

Query unknown data structure in GraphQL

我刚开始使用 GraphQL,目前正在使用 webonyx/graphql-php 设置服务器。由于 GraphQL 查询已经包含结果数据结构,我不太确定如何获取动态数据。假设我查询包含不同元素类型的内容,我的最终结构应该是这样的:

{
    "data": {
        "dataset": {
            "uuid": "abc...",
            "insertDate": "2018-05-04T12:12:12Z",
            // other metadata
            "content": [
                {
                    "type": "headline",
                    "text": "I am a headline"
                },
                {
                    "type": "image",
                    "src": "http://...",
                    "alt": "I am an image"
                },
                {
                    "type": "review",
                    "rating": 3,
                    "comment": "I am a review"
                },
                {
                    "type": "headline",
                    "text": "I am another headline"
                }
                // other content elements
            ]
        }
    }
}

如何为这个例子编写查询?

{
    dataset {
        uuid
        insertDate
        content {
            ????
        }
    }
}

内容部分的类型定义是什么样的?有一组已定义的元素类型(标题、图片、评论等),但它们的顺序和元素数量是未知的,并且它们只有一个共同的字段类型。在我的前端编写查询时,我对内容结构一无所知。内容部分的 graphql-php 类型定义是什么样的?我在网上找不到任何类似的例子,所以我不确定是否可以在这个用例中使用 GraphQL。作为额外信息,我总是想查询整个内容部分,而不是单个元素或字段,而是所有内容。

当您要返回一个对象类型数组,但每个单独的项目可能是任意数量的不同对象类型之一时,您可以使用接口或联合。我们可以在这里使用接口,因为所有实现类型共享一个字段 (type)。

use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;

$content = new InterfaceType([
    'name' => 'Content',
    'description' => 'Available content',
    'fields' => [
        'type' => [
            'type' => Type::nonNull(Type::string()),
            'description' => 'The type of content',
        ]
    ],
    'resolveType' => function ($value) {
        if ($value->type === 'headline') {
            return MyTypes::headline();            
        } elseif ($value->type === 'image') {
            return MyTypes::image();
        } # and so on
    }
]);

实现接口的类型需要在其定义中明确地这样做:

$headline = new ObjectType([
    # other properties 
    'interfaces' => [
        $content
    ]
]);

现在,如果将 content 字段的类型更改为 content 的列表,则可以使用 inline fragments:[=18 仅查询特定于每个实现类型的字段=]

query GetDataset {
  dataset {
    uuid
    insertDate
    content {
      type # this field is shared, so it doesn't need an inline fragment
      ... on Headline {
        text
      }
      ... on Image {
        src
        alt
      }
      # and so on
    }
  }
}

详情请见the docs