Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Связи между моделями

У статьи много комментариев, а каждый комментарий принадлежит одной статье. Такие отношения между таблицами называют связями. В Lucid их объявляют в моделях, и потом комментарии статьи получают одной строкой, без ручного SQL.

Таблица комментариев

Создайте модель вместе с миграцией:

node ace make:model Comment -m

В миграции добавьте внешний ключ — столбец post_id, который ссылается на статью:

// database/migrations/1727970000000_create_comments_table.ts
import { BaseSchema } from '@adonisjs/lucid/schema'
 
export default class extends BaseSchema {
  protected tableName = 'comments'
 
  async up() {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.integer('post_id').unsigned().references('id').inTable('posts').onDelete('CASCADE')
      table.text('body').notNullable()
      table.timestamp('created_at')
      table.timestamp('updated_at')
    })
  }
 
  async down() {
    this.schema.dropTable(this.tableName)
  }
}

onDelete('CASCADE') удалит комментарии вместе со статьёй. Выполните node ace migration:run.

Связь belongsTo

Комментарий принадлежит статье:

// app/models/comment.ts
import { DateTime } from 'luxon'
import { BaseModel, column, belongsTo } from '@adonisjs/lucid/orm'
import type { BelongsTo } from '@adonisjs/lucid/types/relations'
import Post from '#models/post'
 
export default class Comment extends BaseModel {
  @column({ isPrimary: true })
  declare id: number
 
  @column()
  declare postId: number
 
  @column()
  declare body: string
 
  @belongsTo(() => Post)
  declare post: BelongsTo<typeof Post>
 
  @column.dateTime({ autoCreate: true })
  declare createdAt: DateTime
}

Lucid по имени связи post находит внешний ключ postId. Функция () => Post нужна, чтобы модели могли ссылаться друг на друга.

Связь hasMany

Со стороны статьи — много комментариев. Допишите в модель Post:

// app/models/post.ts
import { BaseModel, column, hasMany } from '@adonisjs/lucid/orm'
import type { HasMany } from '@adonisjs/lucid/types/relations'
import Comment from '#models/comment'
 
export default class Post extends BaseModel {
  // ...столбцы из прошлых уроков
 
  @hasMany(() => Comment)
  declare comments: HasMany<typeof Comment>
}

Предзагрузка

Связанные записи загружаются явно методом preload:

// app/controllers/posts_controller.ts
async show({ params, view }: HttpContext) {
  const post = await Post.query().where('id', params.id).preload('comments').firstOrFail()
  return view.render('pages/posts/show', { post })
}
{{-- resources/views/pages/posts/show.edge --}}
<h2>Комментарии ({{ post.comments.length }})</h2>
@each(comment in post.comments)
  <p>{{ comment.body }}</p>
@end

На странице статьи появится список её комментариев. Если модель уже загружена, связь подгружают так: await post.load('comments').

Создание связанной записи

// app/controllers/comments_controller.ts
const post = await Post.findOrFail(params.postId)
await post.related('comments').create({ body: 'Отличная статья!' })

related('comments') сам подставит postId, указывать его вручную не нужно.

Другие связи

ДекораторПример
hasOneУ пользователя один профиль
hasManyУ статьи много комментариев
belongsToКомментарий принадлежит статье
manyToManyСтатьи и теги через промежуточную таблицу

Частая ошибка

Обращение к post.comments без preload или load не делает запрос к базе: свойство равно undefined, и шаблон либо ничего не выведет, либо упадёт на post.comments.length. Lucid никогда не загружает связи сам — всегда предзагружайте нужные связи в контроллере.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.