Relações de entidade
GraphQL consultas podem percorrer objetos relacionados e seus campos, para que com apenas uma consulta você possa escrever algo como:
{
books
{
items {
id
title
authors {
items {
first_name
last_name
}
}
}
}
}
Para recuperar livros e seus autores.
Para permitir que essa capacidade funcione, o Construtor de API de Dados precisa saber como os dois objetos estão relacionados entre si. A relationships
seção no arquivo de configuração fornece os metadados necessários para fazer essa capacidade funcionar de forma correta e eficiente.
Configurando uma relação
Não importa qual banco de dados você esteja usando com o Construtor de API de Dados, você precisa informar explicitamente ao Construtor de API de Dados que um objeto está relacionado a outro. Há três tipos de relações que podem ser estabelecidas entre duas entidades:
Relação um para muitos
Uma relação um-para-muitos permite que um objeto acesse uma lista de objetos relacionados. Por exemplo, uma série de livros pode permitir o acesso a todos os livros dessa série:
{
series {
items {
name
books {
items {
title
}
}
}
}
}
Se houver Chaves Estrangeiras que dão suporte à relação entre os dois objetos de banco de dados subjacentes, você só precisará informar ao Construtor de API de Dados que deseja expor essa relação. Com a CLI do DAB:
dab update Series --relationship books --target.entity Book --cardinality many
O que atualiza a series
entidade – usada no exemplo:
"Series": {
"source": "dbo.series",
...
"relationships": {
"books": {
"target.entity": "Book",
"cardinality": "many"
}
}
...
}
Uma nova chave é adicionada sob o relationships
elemento : books
. O elemento define o nome usado para o campo GraphQL navegar do series
objeto para o objeto definido no target.entity
, Book
nesse caso. Isso significa que deve haver uma entidade chamada Book
no arquivo de configuração.
A cardinality
propriedade informa ao Construtor de API de Dados que pode haver muitos livros em cada série, portanto, o campo GraphQL criado retorna uma lista de itens.
Essa propriedade é tudo o que você precisa. Na inicialização, o Construtor de API de Dados detecta automaticamente os campos de banco de dados que precisam ser usados para sustentar a relação definida.
Se você não tiver uma restrição foreign key que sustenta a relação de banco de dados, o construtor de API de Dados não poderá descobrir automaticamente quais campos são usados. Para informar ao Construtor de API de Dados quais campos relacionam as duas entidades, especifique-as manualmente. Você pode especificá-los com a CLI usando dab update
:
dab update Series --relationship books --target.entity Book --cardinality many --relationship.fields "id:series_id"
A opção relationship.fields
permite definir quais campos são usados da entidade que está sendo atualizada (Series
) e quais campos são usados da entidade de destino (Book
), para conectar os dados de uma entidade à outra.
No exemplo anterior, o id
campo de banco de dados da Series
entidade é correspondido com o campo series_id
de banco de dados da Book
entidade.
A configuração também contém essas informações:
"Series": {
"source": "dbo.series",
...
"relationships": {
"books": {
"cardinality": "many",
"target.entity": "Book",
"source.fields": ["id"],
"target.fields": ["series_id"]
}
}
...
}
Relação muitos para um
Uma relação muitos para um é semelhante à relação um-para-muitos com duas diferenças principais:
- o
cardinality
está definido comoone
- o campo GraphQL criado retorna um escalar e não uma lista
Seguindo os exemplos de Série de Livros usados antes, um livro pode estar em apenas uma série, portanto, a relação é criada usando o seguinte comando da CLI do DAB:
dab update Book --relationship series --target.entity Series --cardinality one
O que gera essa configuração:
"Book": {
"source": "dbo.books",
...
"relationships": {
"series": {
"target.entity": "Series",
"cardinality": "one"
}
}
}
O que, por sua vez, permite uma consulta GraphQL como este exemplo:
{
books {
items {
id
title
series {
name
}
}
}
}
Onde cada livro retorna também a série à qual pertence.
Relação muitos para muitos
Muitos para muitos relacionamentos podem ser vistos como um par de relações um-para-muitos e muitos-para-um trabalhando juntos. Um autor certamente pode escrever mais de um livro (uma relação Um-para-Muitos), mas também é verdade que mais de um autor pode trabalhar no mesmo livro (uma relação Muitos para Um).
O construtor de API de Dados dá suporte a esse tipo de relação nativamente:
- Usando um par de relações Um para Muitos/Muitos para Um.
- Usando um objeto de vinculação.
Usando um par de relações um para muitos/muitos para um
Um requisito comercial que provavelmente estará lá é acompanhar como os royalties são divididos entre os autores de um livro. Para implementar esse requisito, uma entidade dedicada que vincula um autor, um livro e os royalties atribuídos são necessários. Portanto, são necessárias três entidades:
authors
, para representar detalhes biográficos dos autores.books
, para representar dados do livro, como título e NÚMERO do Livro Padrão Internacional (ISBN).books_authors
para representar dados relacionados a um livro e ao seu autor, por exemplo, o percentual de royalties que um autor obtém para um livro específico.
As três entidades podem ser visualizadas por meio do diagrama a seguir.
Como visível, há duas relações bidirecionais:
- Relação um para muitos/muitos para um entre
authors
e obooks_authors
- Relação um para muitos/muitos para um entre
books
e obooks_authors
Para lidar com esse cenário normalmente com o DAB, tudo o que é necessário é criar as entidades e mapeamentos relacionados no arquivo de configuração. Supondo que a Book
entidade e Author
já estejam no arquivo de configuração:
dab add BookAuthor --source dbo.books_authors --permissions "anonymous:*"
Para adicionar a nova entidade, execute dab update
:
dab update Book --relationship authors --target.entity BookAuthor --cardinality many --relationship.fields "id:book_id"
dab update Author --relationship books --target.entity BookAuthor --cardinality many --relationship.fields "id:author_id"
Para adicionar as relações à entidade recém-criada BookAuthor
, execute dab update
novamente:
dab update BookAuthor --relationship book --target.entity Book --cardinality one --relationship.fields "book_id:id"
dab update BookAuthor --relationship author --target.entity Author --cardinality one --relationship.fields "author_id:id"
Para adicionar as relações de BookAuthor
às Book
entidades e Author
. Com a configuração fornecida, o DAB é capaz de lidar com consultas aninhadas como este exemplo:
{
authors {
items {
first_name
last_name
books {
items {
book {
id
title
}
royalties_percentage
}
}
}
}
}
Onde você está pedindo para devolver todos os autores, o livro que eles escreveram junto com os royalties relacionados.
Usando um objeto de vinculação
O processo descrito na seção anterior funciona muito bem se todas as entidades envolvidas nas relações Muitos para Muitos precisarem ser acessadas por meio de GraphQL. Esse cenário nem sempre é o caso. Por exemplo, se você não precisar controlar os royalties, a BookAuthor
entidade não trará nenhum valor para o usuário final. A entidade só foi usada para associar livros a seus autores. Em bancos de dados relacionais, relações muitos para muitos são criadas usando essa terceira tabela que vincula as tabelas que participam da relação Muitos para Muitos:
No diagrama, você pode ver que há uma tabela chamada books_authors
que está vinculando autores com seus livros e livros com seus autores. Essa tabela de vinculação não precisa ser exposta ao usuário final. A tabela de vinculação é apenas um artefato para permitir que a relação Muitos para Muitos exista, mas o Construtor de API de Dados precisa saber sua existência para usá-la corretamente.
A CLI do DAB pode ser usada para criar a relação Muitos para Muitos e também configurar o objeto de vinculação (remova todas as relações criadas na seção anterior e comece apenas com a Book
entidade e Author
sem nenhuma relação configurada entre elas:
dab update Book --relationship authors --target.entity Author --cardinality many --relationship.fields "id:id" --linking.object "dbo.books_authors" --linking.source.fields "book_id" --linking.target.fields "author_id"
O que atualiza o arquivo de configuração JSON para ser semelhante a este exemplo:
"Book": {
"source": "dbo.books",
...
"relationships": {
"authors": {
"cardinality": "many",
"target.entity": "author",
"source.fields": [ "id" ],
"target.fields": [ "id" ],
"linking.object": "dbo.books_authors",
"linking.source.fields": [ "book_id" ],
"linking.target.fields": [ "author_id" ]
}
}
}
A configuração está informando ao DAB que você deseja adicionar um authors
campo na entidade que permite o Book
acesso aos autores do livro. authors
pode ser many
, portanto, uma lista de autores é retornada quando a consulta GraphQL acessa o authors
campo. Essa relação define como navegar de livros para autores: os campos de banco de dados usados para navegar de livros para seus autores são definidos no source.fields
para o livro e, no target.fields
para os autores, de forma semelhante à relação Um para Muitos ou Muitos para Um descrita anteriormente neste artigo.
Essa relação é uma relação Muitos para Muitos, portanto, não há nenhuma conexão direta entre as duas entidades e, portanto, é necessário usar uma linking.object
. No exemplo, a tabela dbo.books_authors
de banco de dados é usada como o objeto de vinculação. Como o objeto de vinculação é capaz de conectar livros a seus autores é definido nas linking.source.fields
propriedades e linking.target.fields
. O primeiro informa ao DAB como a entidade de origem - a Book
- está conectada ao objeto de gosto e a segunda como o objeto de vinculação está conectado à entidade de destino, Author
no exemplo.
Para entender como as informações fornecidas são usadas, você pode usar este exemplo de consulta equivalente:
select *
from dbo.books as b
inner join dbo.books_authors as ba on b.id = ba.book_id
inner join dbo.authors a on ba.author_id = a.id
Com a configuração fornecida, o DAB é capaz de entender GraphQL como este exemplo:
{
books {
items {
id
title
authors {
items {
first_name
last_name
}
}
}
}
}
Onde você quer obter livros e seus autores.
Para permitir a navegação de Author
para Book
, os mesmos princípios podem ser aplicados, atualizando a configuração usando o seguinte comando:
dab update Author --relationship books --target.entity Book --cardinality many --relationship.fields "id:id" --linking.object "dbo.books_authors" --linking.source.fields "author_id" --linking.target.fields "book_id"
Que define uma relação Muitos para Muitos entre a Author
entidade e a Book
entidade, usando o objeto dbo.books_authors
de vinculação nos bastidores.