Encodera

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

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

Оглавление

События и слушатели

Когда к статье добавляют комментарий, нужно сохранить его, отправить письмо автору и, может быть, обновить счётчик. Если всё это писать в контроллере, он разрастается. Событие — сообщение «что-то произошло», а слушатель — код, который на него реагирует. Контроллер только сообщает о событии и не знает, кто и что сделает в ответ.

Класс события

node ace make:event CommentCreated

Появится файл app/events/comment_created.ts. Событие хранит данные о том, что произошло:

// app/events/comment_created.ts
import { BaseEvent } from '@adonisjs/core/events'
import Comment from '#models/comment'
 
export default class CommentCreated extends BaseEvent {
  constructor(public comment: Comment) {
    super()
  }
}

Отправка события

// app/controllers/comments_controller.ts
import CommentCreated from '#events/comment_created'
 
const comment = await post.related('comments').create({ body: payload.body })
CommentCreated.dispatch(comment)

Статический метод dispatch создаёт событие с переданными аргументами и сообщает о нём всем слушателям. Контроллер сразу продолжает работу.

Слушатель

node ace make:listener NotifyPostAuthor
// app/listeners/notify_post_author.ts
import mail from '@adonisjs/mail/services/main'
import CommentCreated from '#events/comment_created'
 
export default class NotifyPostAuthor {
  async handle(event: CommentCreated) {
    const comment = event.comment
    await comment.load('post', (query) => query.preload('author'))
 
    await mail.sendLater((message) => {
      message
        .to(comment.post.author.email)
        .subject('Новый комментарий')
        .html(`<p>К статье «${comment.post.title}» оставили комментарий.</p>`)
    })
  }
}

Метод handle получает объект события. Здесь подразумевается, что у статьи есть связь author с моделью User.

Регистрация слушателя

Связь событий и слушателей описывают в файле start/events.ts. Если его нет, создайте командой node ace make:preload events:

// start/events.ts
import emitter from '@adonisjs/core/services/emitter'
import CommentCreated from '#events/comment_created'
 
const NotifyPostAuthor = () => import('#listeners/notify_post_author')
 
emitter.on(CommentCreated, [NotifyPostAuthor, 'handle'])

Слушатель подключается так же лениво, как контроллер. Теперь после каждого нового комментария автор статьи получит письмо. Чтобы добавить ещё одно действие, достаточно нового слушателя — контроллер менять не нужно.

Простая форма

Для небольших реакций можно передать функцию вместо класса:

// start/events.ts
emitter.on(CommentCreated, (event) => {
  console.log(`Новый комментарий к статье ${event.comment.postId}`)
})

В терминале сервера появится строка о каждом новом комментарии.

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

Ошибка в слушателе не возвращается в контроллер: событие уже отправлено, и посетитель увидит успешный ответ, даже если письмо не ушло. Поэтому в слушатели выносят только второстепенные действия. То, без чего операция считается неудачной, например запись в базу, оставляйте в контроллере.

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

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