Я только начал работать с 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, вы сможете запрашивать только поля, специфичные для каждого типа реализации, используя встроенные фрагменты:
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
}
}
}
Дополнительные сведения см. в статье документы.
Нет, вы должны явно определить каждый тип, а затем явно запрашивать каждый тип при выполнении запроса. С одной стороны, это боль. С другой стороны, он позволяет вам специально запрашивать, какие поля вы хотите использовать для каждого типа. Если у вас есть более одного запроса, который запрашивает тип Content, вы можете использовать фрагменты для уменьшения избыточности.
Если вы действительно против того, чтобы записывать все это, вы могли бы использовать запрос самоанализа, чтобы выяснить все реализующие типы и их поля, а затем использовать это для построения вашего запроса.
Не тот ответ, который я хотел услышать, но это концепция того, как это работает, я уже думал, что это будет так. Спасибо за помощь и полезный пример!
Но поскольку я не знаю, какие типы контента присутствуют, мне приходится добавлять их все в запрос каждый раз, верно? Допустим, у меня есть 50 различных типов, набор данных, который я хочу запросить, имеет только один заголовок (чего не знает мой интерфейс), тогда мне все равно нужно записать все возможные типы и их поля. Есть ли способ сократить это? Я уже читал, что оператора * нет, значит есть другие способы?