Перейти к основному содержанию

Слушатели и подписчики сущностей

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Что такое слушатель сущности?

Любая ваша сущность может содержать методы с пользовательской логикой, которые отслеживают определённые события сущности. Эти методы должны быть помечены специальными декораторами в зависимости от события, которое вы хотите отслеживать.

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

@AfterLoad

Вы можете определить метод с любым именем в сущности и пометить его @AfterLoad. TypeORM будет вызывать его каждый раз при загрузке сущности через QueryBuilder или методы find репозитория/менеджера. Пример:

@Entity()
export class Post {
@AfterLoad()
updateCounters() {
if (this.likesCount === undefined) this.likesCount = 0
}
}

@BeforeInsert

Вы можете определить метод с любым именем в сущности и пометить его @BeforeInsert. TypeORM будет вызывать его перед вставкой сущности через save репозитория/менеджера. Пример:

@Entity()
export class Post {
@BeforeInsert()
updateDates() {
this.createdDate = new Date()
}
}

@AfterInsert

Вы можете определить метод с любым именем в сущности и пометить его @AfterInsert. TypeORM будет вызывать его после вставки сущности через save репозитория/менеджера. Пример:

@Entity()
export class Post {
@AfterInsert()
resetCounters() {
this.counters = 0
}
}

@BeforeUpdate

Вы можете определить метод с любым именем в сущности и пометить его @BeforeUpdate. TypeORM будет вызывать его перед обновлением существующей сущности через save репозитория/менеджера. Учтите, что это происходит только при изменении данных модели. Если вы выполните save без изменений, @BeforeUpdate и @AfterUpdate не сработают. Пример:

@Entity()
export class Post {
@BeforeUpdate()
updateDates() {
this.updatedDate = new Date()
}
}

@AfterUpdate

Вы можете определить метод с любым именем в сущности и пометить его @AfterUpdate. TypeORM будет вызывать его после обновления существующей сущности через save репозитория/менеджера. Пример:

@Entity()
export class Post {
@AfterUpdate()
updateCounters() {
this.counter = 0
}
}

@BeforeRemove

Вы можете определить в сущности метод с любым именем и пометить его @BeforeRemove, и TypeORM вызовет его перед удалением сущности с помощью remove из репозитория/менеджера. Пример:

@Entity()
export class Post {
@BeforeRemove()
updateStatus() {
this.status = "removed"
}
}

@AfterRemove

Вы можете определить метод с любым именем в сущности и пометить его @AfterRemove. TypeORM будет вызывать его после удаления сущности через remove репозитория/менеджера. Пример:

@Entity()
export class Post {
@AfterRemove()
updateStatus() {
this.status = "removed"
}
}

@BeforeSoftRemove

Вы можете определить в сущности метод с любым именем и пометить его @BeforeSoftRemove, и TypeORM вызовет его перед мягким удалением сущности с помощью softRemove из репозитория/менеджера. Пример:

@Entity()
export class Post {
@BeforeSoftRemove()
updateStatus() {
this.status = "soft-removed"
}
}

@AfterSoftRemove

Вы можете определить метод с любым именем в сущности и пометить его @AfterSoftRemove. TypeORM будет вызывать его после мягкого удаления сущности через softRemove репозитория/менеджера. Пример:

@Entity()
export class Post {
@AfterSoftRemove()
updateStatus() {
this.status = "soft-removed"
}
}

@BeforeRecover

Вы можете определить в сущности метод с любым именем и пометить его @BeforeRecover, и TypeORM вызовет его перед восстановлением сущности с помощью recover из репозитория/менеджера. Пример:

@Entity()
export class Post {
@BeforeRecover()
updateStatus() {
this.status = "recovered"
}
}

@AfterRecover

Вы можете определить метод с любым именем в сущности и пометить его @AfterRecover. TypeORM будет вызывать его после восстановления сущности через recover репозитория/менеджера. Пример:

@Entity()
export class Post {
@AfterRecover()
updateStatus() {
this.status = "recovered"
}
}

Что такое подписчик?

Помечает класс как подписчика событий, который может отслеживать события определённых сущностей или любых сущностей. События генерируются через QueryBuilder и методы репозитория/менеджера. Пример:

@EventSubscriber()
export class PostSubscriber implements EntitySubscriberInterface<Post> {
/**
* Indicates that this subscriber only listen to Post events.
*/
listenTo() {
return Post
}

/**
* Called before post insertion.
*/
beforeInsert(event: InsertEvent<Post>) {
console.log(`BEFORE POST INSERTED: `, event.entity)
}
}

Вы можете реализовать любой метод из EntitySubscriberInterface. Для отслеживания любых сущностей просто опустите метод listenTo и используйте any:

@EventSubscriber()
export class PostSubscriber implements EntitySubscriberInterface {
/**
* Called after entity is loaded.
*/
afterLoad(entity: any) {
console.log(`AFTER ENTITY LOADED: `, entity)
}

/**
* Called before query execution.
*/
beforeQuery(event: BeforeQueryEvent<any>) {
console.log(`BEFORE QUERY: `, event.query)
}

/**
* Called after query execution.
*/
afterQuery(event: AfterQueryEvent<any>) {
console.log(`AFTER QUERY: `, event.query)
}

/**
* Called before entity insertion.
*/
beforeInsert(event: InsertEvent<any>) {
console.log(`BEFORE ENTITY INSERTED: `, event.entity)
}

/**
* Called after entity insertion.
*/
afterInsert(event: InsertEvent<any>) {
console.log(`AFTER ENTITY INSERTED: `, event.entity)
}

/**
* Called before entity update.
*/
beforeUpdate(event: UpdateEvent<any>) {
console.log(`BEFORE ENTITY UPDATED: `, event.entity)
}

/**
* Called after entity update.
*/
afterUpdate(event: UpdateEvent<any>) {
console.log(`AFTER ENTITY UPDATED: `, event.entity)
}

/**
* Called before entity removal.
*/
beforeRemove(event: RemoveEvent<any>) {
console.log(
`BEFORE ENTITY WITH ID ${event.entityId} REMOVED: `,
event.entity,
)
}

/**
* Called after entity removal.
*/
afterRemove(event: RemoveEvent<any>) {
console.log(
`AFTER ENTITY WITH ID ${event.entityId} REMOVED: `,
event.entity,
)
}

/**
* Called before entity removal.
*/
beforeSoftRemove(event: SoftRemoveEvent<any>) {
console.log(
`BEFORE ENTITY WITH ID ${event.entityId} SOFT REMOVED: `,
event.entity,
)
}

/**
* Called after entity removal.
*/
afterSoftRemove(event: SoftRemoveEvent<any>) {
console.log(
`AFTER ENTITY WITH ID ${event.entityId} SOFT REMOVED: `,
event.entity,
)
}

/**
* Called before entity recovery.
*/
beforeRecover(event: RecoverEvent<any>) {
console.log(
`BEFORE ENTITY WITH ID ${event.entityId} RECOVERED: `,
event.entity,
)
}

/**
* Called after entity recovery.
*/
afterRecover(event: RecoverEvent<any>) {
console.log(
`AFTER ENTITY WITH ID ${event.entityId} RECOVERED: `,
event.entity,
)
}

/**
* Called before transaction start.
*/
beforeTransactionStart(event: TransactionStartEvent) {
console.log(`BEFORE TRANSACTION STARTED: `, event)
}

/**
* Called after transaction start.
*/
afterTransactionStart(event: TransactionStartEvent) {
console.log(`AFTER TRANSACTION STARTED: `, event)
}

/**
* Called before transaction commit.
*/
beforeTransactionCommit(event: TransactionCommitEvent) {
console.log(`BEFORE TRANSACTION COMMITTED: `, event)
}

/**
* Called after transaction commit.
*/
afterTransactionCommit(event: TransactionCommitEvent) {
console.log(`AFTER TRANSACTION COMMITTED: `, event)
}

/**
* Called before transaction rollback.
*/
beforeTransactionRollback(event: TransactionRollbackEvent) {
console.log(`BEFORE TRANSACTION ROLLBACK: `, event)
}

/**
* Called after transaction rollback.
*/
afterTransactionRollback(event: TransactionRollbackEvent) {
console.log(`AFTER TRANSACTION ROLLBACK: `, event)
}
}

Убедитесь, что свойство subscribers указано в ваших DataSourceOptions, чтобы TypeORM загружал ваших подписчиков.

Event Object

За исключением listenTo, всем методам EntitySubscriberInterface передаётся объект события со следующими базовыми свойствами:

  • dataSource: DataSource - Источник данных, используемый в событии.

  • queryRunner: QueryRunner - QueryRunner, используемый в транзакции события.

  • manager: EntityManager - EntityManager, используемый в транзакции события.

Дополнительные свойства смотрите в каждом интерфейсе событий.

Обратите внимание, что event.entity может не содержать первичные ключи при использовании Repository.update(). Будут доступны только значения из частичного объекта сущности. Чтобы обеспечить наличие первичных ключей в подписчиках, явно передавайте их значения в частичном объекте сущности или используйте Repository.save(), который выполняет повторное получение данных.

await postRepository.update(post.id, { description: "Bacon ipsum dolor amet cow" })

// post.subscriber.ts
afterUpdate(event: UpdateEvent<Post>) {
console.log(event.entity) // outputs { description: 'Bacon ipsum dolor amet cow' }
}

Примечание: Все операции с базой данных в подписанных обработчиках событий должны выполняться через экземпляр queryRunner или manager из объекта события.