Полиморфизм в GraphQL с отношением один ко многим - PullRequest
0 голосов
/ 11 января 2019

В моей схеме есть следующие объекты.

  • Пользователь
  • Ссылка на сообщение
  • Обычный пост

Как мне смоделировать отношения один ко многим между типами User и Post, где Post может быть двух типов LinkPost или NormalPost.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

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

Схема проектирования

Для User очень удобно иметь массив Posts как таковой:

type User {
  id: Int!
  email: String!
  posts: [Posts!]
  username: String!
}

Это означает, что Post должен иметь тип interface или union. Любой из этих двух типов позволяет нам использовать тот факт, что NormalPost & LinkedPost все еще являются типом Post. Это также позволит нам запрашивать их там же, как и в user.posts выше.

Интерфейсы

Тип interface очень похож по поведению на тип interface в ООП. Он определяет базовый набор полей, который также должен иметь любой тип реализации. Например, все Post объекты могут выглядеть так:

interface Post {
  id: Int!
  author: String!
  dateCreated: String!
  dateUpdated: String!
  title: String!
}

Любой тип, который implements интерфейса Post, должен также иметь поля id, author, dateCreated, dateUpdated & title, реализованные в дополнение к любым полям, относящимся к этому типу. Поэтому использование interface, NormalPost & LinkedPost может выглядеть следующим образом:

type NormalPost implements Post {
  id: Int!
  author: String!
  body: String!
  dateCreated: String!
  dateUpdated: String!
  title: String!
}
type LinkedPost implements Post {
  id: Int!
  author: String!
  dateCreated: String!
  dateUpdated: String!
  link: String!
  title: String!
}

Союзы

Тип

A union позволяет разным типам, не требующим реализации аналогичных полей, возвращаться вместе. Тип union структурирован иначе, чем при использовании интерфейсов, поскольку тип union не указывает никаких полей. Единственное, что определено в определении схемы union, - это объединенные типы, разделенные |.

Единственное предостережение к этому правилу - любые типы в union, имеющие одно и то же имя поля, должны иметь одинаковую обнуляемость (т. Е. Поскольку NormalPost.title не может обнуляться (title: String!), тогда LinkedPost.title также не должно обнуляться.

union Post = NormalPost | LinkedPost
type NormalPost implements Post {
  id: Int!
  author: String!
  body: String!
  dateCreated: String!
  dateUpdated: String!
  title: String!
}
type LinkedPost implements Post {
  id: Int!
  author: String!
  dateCreated: String!
  dateUpdated: String!
  link: String!
  title: String!
}

1063 * Запросы * Выше вводится вопрос о том, как отличить LinkedPost от NormalPost при запросе их от user.posts. В обоих случаях вам нужно будет использовать условный фрагмент . Условный фрагмент позволяет запрашивать определенный набор полей типа interface или union. Они выглядят так же, как обычный фрагмент запроса, так как они определены с использованием синтаксиса ... on <Type> в вашем теле запроса. Существует небольшая разница в том, как условный фрагмент может быть структурирован для типа interface против union. В дополнение к запросам с использованием условного фрагмента полезно добавить в свои полиморфные запросы __typename метаполе , чтобы потребитель запроса мог лучше определить тип получающегося объекта в код. Интерфейсы Так как interface определяет конкретный набор полей, которые объединяют все реализующие типы, эти поля можно запрашивать, как и любой другой обычный запрос к типу. Разница заключается в том, что типы interface имеют разные поля, специфичные для их типа, например NormalPost.body против LinkedPost.link. Запрос, который выбирает весь интерфейс Post, а затем NormalPost.body и LinkedPost.link, будет выглядеть следующим образом: query getUsersNormalAndLinkedPosts { user(id: 123) { id name username posts { __typename id author dateCreated dateUpdated title ... on NormalPost { body } ... on LinkedPost { link } } } } Союзы Поскольку union не определяет общие поля между его типами, все поля, которые должны быть выбраны, должны существовать в каждом условном фрагменте. Это единственная разница между запросами interface и union. Запрос типа union выглядит следующим образом: query getUsersNormalAndLinkedPosts { user(id: 123) { id name username posts { __typename ... on NormalPost { id author body dateCreated dateUpdated title } ... on LinkedPost { id author dateCreated dateUpdated link title } } } } Заключение

Оба полиморфных типа имеют свои сильные и слабые стороны, и вам решать, какой из них лучше всего подходит для вашего случая использования. Я использовал как при построении схемы GraphQL, так и особую разницу при использовании interface или union, если есть общие поля между типами реализации.

Интерфейсы имеют большой смысл, когда единственное различие между реализующими типами состоит только в небольшом наборе полей, в то время как остальные разделены между ними. Это приводит к меньшим запросам и потенциально меньшему количеству условных фрагментов.

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

union SearchResult = User | Post.

Это может быть возвращено из запроса с подписью

search(phrase: String!): [SearchResult!]

В контексте этого конкретного вопроса я бы остановился на подходе interface, так как он наиболее целесообразен с точки зрения отношений и запросов.

0 голосов
/ 11 января 2019

Считаете ли вы, что у вас есть единственная сущность Post и вы определили тип записи с помощью типа enum?

Пользователь

type User {
  id: ID!
  posts: [Post!]!
}

Сообщение

type Post {
  id: ID!
  title: String!
  createdBy: User!
  type: PostType!
}

и тип enum для Post

PostType

enum PostType {
  NORMAL
  LINK
}

----- Обновление! -----

Если вы хотите разделить сущности, вы можете просто сделать это:

Пользователь

type User {
  id: ID!
  linkPosts: [LinkPost!]!
  normalPosts: [NormalPost]
}

LinkPost тип

type LinkPost {
  description: String!
  url: String!
  createdBy: User!
}

NormalPost type

type NormalPost {
  title: String!
  description: String!
  createdBy: User!
}

Дайте мне знать, если это работает для вас,

Привет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...