Запросить неизвестную структуру данных в 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 для этого варианта использования. В качестве дополнительной информации я всегда хочу запрашивать весь раздел содержимого, а не отдельный элемент или поле, всегда все.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Что такое Apollo Client и зачем он нужен?
Что такое Apollo Client и зачем он нужен?
Apollo Client - это полнофункциональный клиент GraphQL для JavaScript-приложений, который упрощает получение, управление и обновление данных в...
1
0
734
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Когда вы возвращаете массив типов объектов, но каждый отдельный элемент может быть одним из любого количества различных типов объектов, вы можете использовать либо интерфейс, либо объединение. Здесь мы можем использовать интерфейс, так как все реализующие типы имеют общее поле (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, вы сможете запрашивать только поля, специфичные для каждого типа реализации, используя встроенные фрагменты:

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
    }
  }
}

Дополнительные сведения см. в статье документы.

Но поскольку я не знаю, какие типы контента присутствуют, мне приходится добавлять их все в запрос каждый раз, верно? Допустим, у меня есть 50 различных типов, набор данных, который я хочу запросить, имеет только один заголовок (чего не знает мой интерфейс), тогда мне все равно нужно записать все возможные типы и их поля. Есть ли способ сократить это? Я уже читал, что оператора * нет, значит есть другие способы?

LaDeSe 25.01.2019 14:48

Нет, вы должны явно определить каждый тип, а затем явно запрашивать каждый тип при выполнении запроса. С одной стороны, это боль. С другой стороны, он позволяет вам специально запрашивать, какие поля вы хотите использовать для каждого типа. Если у вас есть более одного запроса, который запрашивает тип Content, вы можете использовать фрагменты для уменьшения избыточности.

Daniel Rearden 25.01.2019 15:00

Если вы действительно против того, чтобы записывать все это, вы могли бы использовать запрос самоанализа, чтобы выяснить все реализующие типы и их поля, а затем использовать это для построения вашего запроса.

Daniel Rearden 25.01.2019 15:02

Не тот ответ, который я хотел услышать, но это концепция того, как это работает, я уже думал, что это будет так. Спасибо за помощь и полезный пример!

LaDeSe 25.01.2019 15:04

Другие вопросы по теме