Связи между моделями
У статьи много комментариев, а каждый комментарий принадлежит одной статье. Такие отношения между таблицами называют связями. В 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 никогда не загружает связи сам — всегда предзагружайте нужные связи в контроллере.