Relacionamentos
Os modelos de dados Lucid têm suporte pronto para uso para trabalhar com relacionamentos. Você precisa definir os relacionamentos em seus modelos, e o Lucid fará todo o trabalho pesado de construir as consultas SQL subjacentes.
HasOne
HasOne cria um relacionamento um-para-um
entre dois modelos. Por exemplo, Um usuário tem um perfil. O relacionamento has one
precisa de uma chave estrangeira na tabela relacionada.
A seguir está um exemplo de estrutura de tabela para o relacionamento has one
. profiles.user_id
é a chave estrangeira e forma o relacionamento com a coluna users.id
.
A seguir estão os exemplos de migrações para as tabelas users
e profiles
.
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Users extends BaseSchema {
protected tableName = 'users'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Profiles extends BaseSchema {
protected tableName = 'profiles'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE') // excluir perfil quando o usuário for excluído
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
Definindo relacionamento no modelo
Depois de criar as tabelas com as colunas necessárias, você também terá que definir o relacionamento no modelo Lucid.
O relacionamento has one é definido usando o decorador @hasOne em uma propriedade do modelo.
import Profile from '#models/profile'
import type { HasOne } from '@adonisjs/lucid/types/relations'
import { column, BaseModel, hasOne } from '@adonisjs/lucid/orm'
export default class User extends BaseModel {
@hasOne(() => Profile)
declare profile: HasOne<typeof Profile>
}
Chaves de relacionamento personalizadas
Por padrão, foreignKey
é a representação snake_case do nome do modelo pai e sua chave primária. No entanto, você também pode definir uma chave estrangeira personalizada.
@hasOne(() => Profile, {
foreignKey: 'profileUserId', // padrão para userId
})
declare profile: HasOne<typeof Profile>
NOTA
Lembre-se, se você pretende usar camelCase para sua definição de chave estrangeira, tenha em mente que a estratégia de nomenclatura padrão irá convertê-la automaticamente para snake_case.
A chave local é sempre a chave primária do modelo pai, mas também pode ser definida explicitamente.
@hasOne(() => Profile, {
localKey: 'uuid', // padrão para id
})
declare profile: HasOne<typeof Profile>
HasMany
HasMany cria um relacionamento um-para-muitos
entre dois modelos. Por exemplo, Um usuário tem muitas postagens. O relacionamento hasMany precisa de uma chave estrangeira na tabela relacionada.
A seguir está um exemplo de estrutura de tabela para o relacionamento hasMany. posts.user_id
é a chave estrangeira e forma o relacionamento com a coluna users.id
.
A seguir estão os exemplos de migrações para as tabelas users
e posts
.
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Users extends BaseSchema {
protected tableName = 'users'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Posts extends BaseSchema {
protected tableName = 'posts'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE') // excluir postagem quando o usuário for excluído
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
Definindo relacionamento no modelo
Depois de criar as tabelas com as colunas necessárias, você também terá que definir o relacionamento no modelo Lucid.
O relacionamento has many é definido usando o decorador @hasMany em uma propriedade do modelo.
import Post from '#models/post'
import type { HasMany } from '@adonisjs/lucid/types/relations'
import { column, BaseModel, hasMany } from '@adonisjs/lucid/orm'
export default class User extends BaseModel {
@hasMany(() => Post)
declare posts: HasMany<typeof Post>
}
Chaves de relacionamento personalizadas
Por padrão, foreignKey
é a representação camelCase do nome do modelo pai e sua chave primária. No entanto, você também pode definir uma chave estrangeira personalizada.
@hasMany(() => Post, {
foreignKey: 'authorId', // padrão para userId
})
declare posts: HasMany<typeof Post>
:::note Lembre-se, se você pretende usar camelCase para sua definição de chave estrangeira, tenha em mente que a estratégia de nomenclatura padrão irá convertê-la automaticamente para snake_case. :::
A chave local é sempre a chave primária do modelo pai, mas também pode ser definida explicitamente.
@hasMany(() => Post, {
localKey: 'uuid', // padrão para id
})
declare posts: HasMany<typeof Post>
BelongsTo
BelongsTo é o inverso do relacionamento hasOne
e hasMany
. Então, por exemplo, profile pertence a um usuário e a post pertence a um usuário.
Você pode aproveitar a mesma estrutura de tabela e as mesmas convenções de chave estrangeira para definir um relacionamento belongTo.
O relacionamento pertence a é definido usando o decorador @belongsTo em uma propriedade de modelo.
import User from '#models/user'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import { column, BaseModel, belongsTo } from '@adonisjs/lucid/orm'
export default class Profile extends BaseModel {
// A chave estrangeira ainda está no mesmo modelo
@column()
declare userId: number
@belongsTo(() => User)
declare user: BelongsTo<typeof User>
}
ManyToMany
Um relacionamento muitos-para-muitos é um pouco complexo, pois permite que ambos os lados tenham mais de um relacionamento entre si. Por exemplo: Um usuário pode ter muitas habilidades, e uma habilidade também pode pertencer a muitos usuários.
Você precisa de uma terceira tabela (geralmente conhecida como tabela dinâmica) para que esse relacionamento funcione. A tabela dinâmica contém as chaves estrangeiras para as outras duas tabelas.
No exemplo a seguir, a tabela skill_user
tem as chaves estrangeiras para as tabelas users
e skills
, permitindo que cada usuário tenha muitas habilidades e vice-versa.
A seguir estão os exemplos de migrações para as tabelas users
, skills
e skill_user
.
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Users extends BaseSchema {
protected tableName = 'users'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Skills extends BaseSchema {
protected tableName = 'skills'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class SkillUsers extends BaseSchema {
protected tableName = 'skill_user'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.integer('user_id').unsigned().references('users.id')
table.integer('skill_id').unsigned().references('skills.id')
table.unique(['user_id', 'skill_id'])
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
Definindo relacionamento no modelo
Depois de criar as tabelas com as colunas necessárias, você também terá que definir o relacionamento no modelo Lucid.
O relacionamento muitos para muitos é definido usando o decorador @manyToMany em uma propriedade do modelo.
NOTA
Não há necessidade de criar um modelo para a tabela dinâmica.
import Skill from '#models/skill'
import type { ManyToMany } from '@adonisjs/lucid/types/relations'
import { column, BaseModel, manyToMany } from '@adonisjs/lucid/orm'
export default class User extends BaseModel {
@column({ isPrimary: true })
declare id: number
@manyToMany(() => Skill)
declare skills: ManyToMany<typeof Skill>
}
Chaves de relacionamento personalizadas
Um relacionamento manyToMany depende de muitas chaves diferentes para configurar o relacionamento corretamente. Todas essas chaves são computadas usando convenções padrão. No entanto, você tem a liberdade de substituí-las.
localKey
é a chave primária do modelo pai (ou seja, Usuário)relatedKey
é a chave primária do modelo relacionado (ou seja, Habilidade)pivotForeignKey
é a chave estrangeira para estabelecer o relacionamento com o modelo pai. O valor padrão é a versãosnake_case
do nome do modelo pai e sua chave primária.pivotRelatedForeignKey
é a chave estrangeira para estabelecer o relacionamento com o modelo relacionado. O valor padrão é a versãosnake_case
do nome do modelo relacionado e sua chave primária.
@manyToMany(() => Skill, {
localKey: 'id',
pivotForeignKey: 'user_id',
relatedKey: 'id',
pivotRelatedForeignKey: 'skill_id',
})
declare skills: ManyToMany<typeof Skill>
NOTA
Lembre-se, se você pretende usar camelCase para sua definição de chave estrangeira, tenha em mente que a estratégia de nomenclatura padrão irá convertê-la automaticamente para snake_case.
Tabela dinâmica personalizada
O valor padrão para o nome da tabela dinâmica é calculado pela classificação e, em seguida, pela combinação do nome do modelo pai e do nome do modelo relacionado. No entanto, você também pode definir uma tabela dinâmica personalizada.
@manyToMany(() => Skill, {
pivotTable: 'user_skills',
})
declare skills: ManyToMany<typeof Skill>
Colunas dinâmicas adicionais
Às vezes, sua tabela dinâmica terá colunas adicionais. Por exemplo, você está armazenando a proficiência
junto com a habilidade do usuário.
Você terá que informar um relacionamento manyToMany sobre essa coluna extra. Caso contrário, o Lucid não a selecionará durante as consultas de busca.
@manyToMany(() => Skill, {
pivotColumns: ['proficiency'],
})
declare skills: ManyToMany<typeof Skill>
Carimbos de data/hora da tabela dinâmica
Você pode habilitar o suporte para carimbos de data/hora criado em e atualizado em para suas tabelas dinâmicas usando a propriedade pivotTimestamps
.
- Uma vez definido, o Lucid definirá/atualizará automaticamente esses carimbos de data/hora em consultas de inserção e atualização.
- Luxon Datetime classe durante a busca.
@manyToMany(() => Skill, {
pivotTimestamps: true
})
declare skills: ManyToMany<typeof Skill>
As configurações pivotTimestamps = true
pressupõem que os nomes das colunas sejam definidos como created_at
e updated_at
. No entanto, você também pode definir nomes de colunas personalizados.
@manyToMany(() => Skill, {
pivotTimestamps: {
createdAt: 'creation_date',
updatedAt: 'updation_date'
}
})
declare skills: ManyToMany<typeof Skill>
Para desabilitar um carimbo de data/hora específico, você pode definir seu valor como false
.
@manyToMany(() => Skill, {
pivotTimestamps: {
createdAt: 'creation_date',
updatedAt: false // desativar atualização no campo de registro de data e hora
}
})
skills: ManyToMany<typeof Skill>
HasManyThrough
O relacionamento HasManyThrough é semelhante ao relacionamento HasMany
, mas cria o relacionamento por meio de um modelo intermediário. Por exemplo, Um país tem muitos posts por meio de usuários.
- Este relacionamento precisa que o modelo through (ou seja, Usuário) tenha uma referência de chave estrangeira com o modelo atual (ou seja, País). O modelo relacionado (ou seja, Post) tem uma referência de chave estrangeira com o modelo through (ou seja, Usuário).
A seguir estão os exemplos de migrações para as tabelas countries
, users
e posts
.
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Countries extends BaseSchema {
protected tableName = 'countries'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Users extends BaseSchema {
protected tableName = 'users'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.integer('country_id').unsigned().references('countries.id')
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class Posts extends BaseSchema {
protected tableName = 'posts'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.integer('user_id').unsigned().references('users.id')
table.timestamp('created_at', { useTz: true })
table.timestamp('updated_at', { useTz: true })
})
}
}
Definindo relacionamento no modelo
Depois de criar as tabelas com as colunas necessárias, você também terá que definir o relacionamento no modelo Lucid.
O relacionamento has many through é definido usando o decorador @hasManyThrough em uma propriedade do modelo.
import Post from '#models/post'
import User from '#models/user'
import type { HasManyThrough } from '@adonisjs/lucid/types/relations'
import { column, BaseModel, hasManyThrough } from '@adonisjs/lucid/orm'
export default class Country extends BaseModel {
@column({ isPrimary: true })
declare id: number
@hasManyThrough([() => Post, () => User])
declare posts: HasManyThrough<typeof Post>
}
Relacionamento de pré-carregamento
O pré-carregamento permite que você busque os dados do relacionamento junto com a consulta principal. Por exemplo: selecione todos os usuários e preload
seus perfis ao mesmo tempo.
- O método
preload
aceita o nome do relacionamento definido no modelo. - O valor da propriedade do relacionamento para o relacionamento
hasOne
ebelongsTo
é definido como a instância do modelo relacionada ounull
quando nenhum registro é encontrado. O valor da propriedade de relacionamento é uma matriz da instância do modelo relacionado para todos os outros tipos de relacionamento.
const users = await User.query().preload('profile')
users.forEach((user) => {
console.log(user.profile)
})
Você pode modificar a consulta de relacionamento passando um retorno de chamada opcional para o método preload
.
const users = await User.query().preload('profile', (profileQuery) => {
profileQuery.where('isActive', true)
})
Pré-carregar vários relacionamentos
Você pode ``pré-carregarvários relacionamentos juntos chamando o método
preload` várias vezes. Por exemplo:
const users = await User.query().preload('profile').preload('posts')
Pré-carregar relacionamentos aninhados
Você pode pré-carregar relacionamentos aninhados usando o construtor de consulta de relacionamento acessível por meio do retorno de chamada opcional.
No exemplo a seguir, buscamos todos os usuários, pré-carregamos suas postagens e, em seguida, buscamos todos os comentários para cada postagem, junto com o usuário do comentário.
const users = await User.query().preload('posts', (postsQuery) => {
postsQuery.preload('comments', (commentsQuery) => {
commentsQuery.preload('user')
})
})
Colunas pivot de muitos para muitos
Ao pré-carregar um relacionamento manyToMany, as colunas da tabela dinâmica são movidas para o objeto $extras
na instância do relacionamento.
Por padrão, selecionamos apenas as chaves estrangeiras da tabela dinâmica. No entanto, você pode definir colunas pivot adicionais para selecionar em definindo o relacionamento ou em tempo de execução.
const users = await User.query().preload('skills', (query) => {
query.pivotColumns(['proficiency'])
})
users.forEach((user) => {
user.skills.forEach((skill) => {
console.log(skill.$extras.pivot_proficiency)
console.log(skill.$extras.pivot_user_id)
console.log(skill.$extras.pivot_skill_id)
console.log(skill.$extras.pivot_created_at)
})
})
Relacionamentos de carregamento lento
Junto com o pré-carregamento, você também pode carregar relacionamentos diretamente de uma instância de modelo.
const user = await User.find(1)
// Carregar o perfil de forma preguiçosa
await user.load('profile')
console.log(user.profile) // Profile | null
// Carregar as postagens de forma preguiçosa
await user.load('posts')
console.log(user.posts) // Post[]
Assim como o método preload
, o método load
também aceita um retorno de chamada opcional para modificar a consulta de relacionamento.
await user.load('profile', (profileQuery) => {
profileQuery.where('isActive', true)
})
Você pode carregar vários relacionamentos chamando o método load
várias vezes ou pegando uma instância do carregador de relacionamento subjacente.
// Chamar o método "load" várias vezes
await user.load('profile')
await user.load('posts')
// Usar o carregador de relacionamentos
await user.load((loader) => {
loader.load('profile').load('posts')
})
Limitar relacionamentos pré-carregados
Digamos que você queira carregar todas as postagens e buscar os três comentários recentes para cada postagem.
Usar o método limit
do construtor de consultas não fornecerá a saída desejada, pois o limite é aplicado a todo o conjunto de dados e não aos comentários de uma postagem individual.
Portanto, você deve usar o método groupLimit
que usa funções de janela SQL para aplicar um limite em cada registro pai separadamente.
const posts = await Post.query().preload('comments', (query) => {
query.groupLimit(3)
})
Construtor de consultas de relacionamento
Você também pode acessar o construtor de consultas para um relacionamento usando o método related
. As consultas de relacionamento sempre têm como escopo uma determinada instância do modelo pai.
O Lucid adicionará automaticamente a cláusula where
para limitar as postagens ao usuário fornecido no exemplo a seguir.
const user = await User.find(1)
const posts = await user.related('posts').query()
O método query
retorna uma instância padrão do construtor de consultas, e você pode encadear quaisquer métodos a ele para adicionar restrições adicionais.
const posts = await user.related('posts').query().where('isPublished', true).paginate(1)
Você também pode usar o construtor de consultas de relacionamento para atualizar
e excluir
linhas relacionadas. No entanto, fazer isso não executará nenhum dos ganchos do modelo.
Filtrar por relacionamentos
Você também pode filtrar os registros da consulta principal verificando a existência ou ausência de um relacionamento. Por exemplo, selecione todas as postagens que receberam um ou mais comentários.
Você pode filtrar por relacionamento usando os métodos has
ou whereHas
. Eles aceitam o nome do relacionamento como o primeiro argumento. Opcionalmente, você também pode passar um operador e o número de linhas esperadas.
// Obter postagens com um ou mais comentários
const posts = await Post.query().has('comments')
// Obter postagens com mais de 2 comentários
const posts = await Post.query().has('comments', '>', 2)
Você pode usar o método whereHas
para adicionar restrições adicionais para a consulta de relacionamento. No exemplo a seguir, buscamos apenas postagens que tenham um ou mais comentários aprovados.
const posts = await Post.query().whereHas('comments', (query) => {
query.where('isApproved', true)
})
Semelhante ao método has
, o whereHas
também aceita um operador opcional e a contagem de linhas esperadas.
const posts = await Post.query().whereHas(
'comments',
(query) => {
query.where('isApproved', true)
},
'>',
2
)
A seguir está a lista de variações de has
e whereHas
.
orHas | orWhereHas
adiciona uma cláusula OR para a existência do relacionamento.doesntHave | whereDoesntHave
verifica a ausência do relacionamento.orDoesntHave | orWhereDoesntHave
adiciona uma cláusula OR para a ausência do relacionamento.
Agregados de relacionamento
A API de relacionamentos do Lucid também permite que você carregue os agregados para relacionamentos. Por exemplo, você pode buscar uma lista de postagens com uma contagem de comentários para cada postagem.
withAggregate
O método withAggregate
aceita o relacionamento como o primeiro argumento e um retorno de chamada obrigatório para definir a função de agregação do valor e o nome da propriedade.
NOTA
No exemplo a seguir, a propriedade comments_count
é movida para o objeto $extras
porque não está definida como uma propriedade no modelo.
const posts = await Post.query().withAggregate('comments', (query) => {
query.count('*').as('comments_count')
})
posts.forEach((post) => {
console.log(post.$extras.comments_count)
})
withCount
Como a contagem de linhas de relacionamento é um requisito muito comum, você pode usar o método withCount
.
const posts = await Post.query().withCount('comments')
posts.forEach((post) => {
console.log(post.$extras.comments_count)
})
Você também pode fornecer um nome personalizado para a propriedade count usando o método as
.
const posts = await Post.query()
.withCount('comments', (query) => {
query.as('commentsCount')
})
posts.forEach((post) => {
console.log(post.$extras.commentsCount)
})
Você pode definir restrições para a consulta de contagem passando um retorno de chamada opcional para o método withCount
.
const posts = await Post.query().withCount('comments', (query) => {
query.where('isApproved', true)
})
Agregados de relacionamento de carregamento lento
Semelhante aos métodos withCount
e withAggregate
, você também pode carregar lentamente os agregados de uma instância de modelo usando os métodos loadCount
e loadAggregate
.
const post = await Post.findOrFail()
await post.loadCount('comments')
console.log(post.$extras.comments_count)
const post = await Post.findOrFail()
await post.loadAggregate('comments', (query) => {
query.count('*').as('commentsCount')
})
console.log(post.$extras.commentsCount)
Certifique-se de usar o método loadCount
somente ao trabalhar com uma única instância de modelo. Se houver várias instâncias de modelo, é melhor usar o método withCount
do construtor de consultas.
Gancho de consulta de relacionamento
Você pode definir um gancho de relacionamento onQuery
no momento de definir um relacionamento. Em seguida, os ganchos de consulta são executados para todas as consultas select, update e delete executadas pelo construtor de consultas de relacionamento.
O método onQuery
geralmente é útil quando você sempre aplica certas restrições à consulta de relacionamento.
import UserEmail from '#models/user_email'
import type { HasMany } from '@adonisjs/lucid/types/model'
import { column, BaseModel, hasMany } from '@adonisjs/lucid/orm'
export default class User extends BaseModel {
@hasMany(() => UserEmail)
declare emails: HasMany<typeof UserEmail>
@hasMany(() => UserEmail, {
onQuery: (query) => {
query.where('isActive', true)
},
})
declare activeEmails: HasMany<typeof UserEmail>
}
Criar relacionamentos
Você pode criar relacionamentos entre dois modelos usando a API de persistência de relacionamentos.
create
No exemplo a seguir, criamos um novo comentário e o vinculamos à postagem ao mesmo tempo. O método create
aceita um objeto JavaScript simples para persistir. O valor da chave estrangeira é definido automaticamente.
const post = await Post.findOrFail(1)
const comment = await post.related('comments').create({
body: 'This is a great post',
})
console.log(comment.postId === post.id) // true
save
A seguir está um exemplo usando o método save
. O método save
precisa de uma instância do modelo relacionado. O valor da chave estrangeira é definido automaticamente.
const post = await Post.findOrFail(1)
const comment = new Comment()
comment.body = 'This is a great post'
await post.related('comments').save(comment)
console.log(comment.postId === post.id) // true
createMany
Você também pode criar vários relacionamentos usando o método createMany
. O método está disponível apenas para relacionamentos hasMany
e manyToMany
.
O método createMany
retorna uma matriz de instâncias de modelo persistentes.
const comments = await Post.related('comments').createMany([
{
body: 'This is a great post.',
},
{
body: 'Well written.',
},
])
saveMany
Semelhante ao método save
. O método saveMany
permite persistir vários relacionamentos juntos.
const comment1 = new Comment()
comment1.body = 'This is a great post'
const comment2 = new Comment()
comment2.body = 'Well written'
await Post.related('comments').saveMany([comment1, comment2])
associate
O método associate
é exclusivo para o relacionamento belongsTo
. Ele permite que você associe dois modelos um ao outro.
const user = await User.findOrFail(1)
const profile = new Profile()
profile.avatarUrl = 'foo.jpg'
await profile.related('user').associate(user)
dissociate
O dissociate
remove o relacionamento definindo a chave estrangeira como null
. Portanto, o método é exclusivo para o relacionamento belongsTo
.
await profile = await Profile.findOrFail(1)
await profile.related('user').dissociate()
attach
O método attach
é exclusivo para um relacionamento manyToMany
. Ele permite que você crie um relacionamento entre dois modelos persistentes dentro da tabela dinâmica.
O método attach
precisa apenas do id
do modelo relacionado para formar o relacionamento dentro da tabela dinâmica.
const user = await User.find(1)
const skill = await Skill.find(1)
// Executar consulta de inserção dentro da tabela dinâmica
await user.related('skills').attach([skill.id])
Você pode definir colunas dinâmicas adicionais passando um objeto de par chave-valor. A chave é o id do modelo relacionado e o valor é um objeto de colunas adicionais.
await user.related('skills').attach({
[skill.id]: {
proficiency: 'Beginner',
},
})
detach
O método detach
é o oposto do método attach
e permite que você remova o relacionamento da tabela dinâmica.
Ele aceita opcionalmente uma matriz de ids
para remover. Chamar o método sem argumentos removerá todos os relacionamentos da tabela dinâmica.
const user = await User.find(1)
const skill = await Skill.find(1)
await user.related('skills').detach([skill.id])
// Remover todas as habilidades do usuário
await user.related('skills').detach()
sync
O método sync
permite que você sincronize as linhas dinâmicas. A carga útil fornecida ao método sync
é considerada a fonte da verdade, e calculamos um diff internamente para executar as seguintes consultas SQL.
- Insira as linhas ausentes na tabela dinâmica, mas presentes na carga útil de sincronização.
- Atualize as linhas presentes na tabela dinâmica e na carga útil de sincronização, mas tenha um ou mais argumentos alterados.
- Remova as linhas presentes na tabela dinâmica, mas ausentes na carga útil de sincronização.
- Ignore as linhas presentes na tabela dinâmica e na carga útil de sincronização.
const user = await User.find(1)
// Somente habilidades com id 1, 2, 3 permanecerão na tabela dinâmica
await user.related('skills').sync([1, 2, 3])
Você também pode definir colunas de pivô adicionais como um objeto de par de chave-valor.
const user = await User.find(1)
await user.related('skills').sync({
[1]: {
proficiency: 'Beginner',
},
[2]: {
proficiency: 'Master',
},
[3]: {
proficiency: 'Master',
},
})
Você pode desabilitar a opção detach
para sincronizar linhas sem remover nenhuma linha da tabela dinâmica.
await user
.related('skills')
// Adicionar habilidades com id 1,2,3, mas não remover nenhuma
// linha da tabela dinâmica
.sync([1, 2, 3], false)
Excluir relacionamento
Na maioria das vezes, você pode excluir linhas relacionadas diretamente do modelo. Por exemplo: Você pode excluir um comentário pelo seu id, diretamente usando o modelo Comment, não há necessidade de acionar a exclusão do comentário via post.
- Para um relacionamento
manyToMany
, você pode usar o métododetach
para remover a linha da tabela dinâmica. - Use o método
dissociate
para remover um relacionamento belongTo sem excluir a linha da tabela do banco de dados.
Usando a ação onDelete
Você também pode usar a ação onDelete
do banco de dados para remover os dados relacionados do banco de dados. Por exemplo: Exclua as postagens de um usuário quando o próprio usuário for excluído.
A seguir, um exemplo de migração para definir a ação onDelete
.
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table
.integer('user_id')
.unsigned()
.references('users.id')
.onDelete('CASCADE')
})