typescript - Cum să actualizezi automat și să obții o coloană de relație bazată pe altă relație
Încerc să conectez 3 relații - Photo, PhotoComment și PhotoRating. Vreau să le ofer utilizatorilor mei posibilitatea de a evalua o fotografie. Aceste fotografii pot avea mai multe comentarii de la mulți utilizatori și mai multe evaluări de la mulți utilizatori (dar doar 1 evaluare per utilizator specific). Și vreau să arăt acea evaluare a utilizatorului pe toate comentariile care aparțin fotografiilor. Totuși, nu reușesc să fac să funcționeze afișarea unei evaluări în comentariu și actualizarea ei automat ori de câte ori utilizatorul schimbă evaluarea fotografiei (folosește metoda ratePhoto)
Când apelez metoda ratePhoto, pot vedea în PGAdmin că photoRatingId nu este conectat cu PhotoComment și nu actualizează nota acolo. Totuși, metoda ratePhoto funcționează corect și salvează nota pentru Photo în mod corespunzător
Folosesc NestJS, TypeORM, PostgreSQL și RxJS. Prefer mult să lucrez pe repository în loc de queryBuilder
photos.service.tspublic ratePhoto(photoId: number, ratePhotoDto: RatePhotoDto, user: User): Observable<any> {
return this.getPhoto(photoId).pipe(
switchMap((photo: Photo) => from(this.photoRatingRepository.findOneBy({ photo: { id: photo.id }, user: { id: user.id } })).pipe(
map((photoRating: PhotoRating) => {
if (!photoRating) {
const newRating: PhotoRating = new PhotoRating();
newRating.rate = ratePhotoDto.rate;
newRating.user = user;
newRating.photo = photo;
return this.photoRatingRepository.save(newRating);
}
photoRating.rate = ratePhotoDto.rate;
return this.photoRatingRepository.save(photoRating);
}),
)),
);
}
photo.entity.ts@Entity()
export class Photo {
//...
@OneToMany(() => PhotoComment, (photoComment: PhotoComment) => photoComment.photo)
public photoComments: PhotoComment[];
@OneToMany(() => PhotoRating, (photoRating: PhotoRating) => photoRating.photo)
public photoRatings: PhotoRating[];
}
photo-comment.entity.ts@Entity()
export class PhotoComment {
//...
@ManyToOne(() => Photo, (photo: Photo) => photo.photoComments, { onDelete: 'CASCADE' })
public photo: Photo;
@ManyToOne(() => PhotoRating, (photoRating: PhotoRating) => photoRating.photoComment)
@JoinColumn({ name: 'rate' })
public photoRating: PhotoRating;
}
photo-rating.entity.ts@Entity()
@Unique(['user', 'photo'])
export class PhotoRating {
//...
@Transform(({ value }) => +value)
@Column({ type: 'decimal', precision: 3, scale: 2, default: 0 })
public rate: number;
@ManyToOne(() => Photo, (photo: Photo) => photo.photoRatings, { onDelete: 'CASCADE' })
public photo: Photo;
@OneToMany(() => PhotoComment, (photoComment: PhotoComment) => photoComment.photoRating, { cascade: true })
public photoComment: PhotoComment;
}
Ce fac greșit?
Răspuns #1
În opinia mea, problema este că nu ați setat photoComment
proprietate în entitatea photoRating. Pentru a face acest lucru, adăugați următorul cod în metoda ratePhoto
photoRating.photoComment = photoComment;
Acest lucru va asigura că proprietatea photoComment a entității photoRating este setată corect.
Metoda ar trebui să fie următoarea
public ratePhoto(photoId: number, ratePhotoDto: RatePhotoDto, user: User): Observable<any> {
return this.getPhoto(photoId).pipe(
switchMap((photo: Photo) => from(this.photoRatingRepository.findOneBy({ photo: { id: photo.id }, user: { id: user.id } })).pipe(
map((photoRating: PhotoRating) => {
if (!photoRating) {
const newRating: PhotoRating = new PhotoRating();
newRating.rate = ratePhotoDto.rate;
newRating.user = user;
newRating.photo = photo;
return this.photoRatingRepository.save(newRating);
}
photoRating.rate = ratePhotoDto.rate;
photoRating.photoComment = photoComment;
return this.photoRatingRepository.save(photoRating);
}),
)),
);
}
Răspunsul #2
Ok, am rezolvat. Majoritatea lucrurilor au fost făcute corect, însă pentru că nu actualizam entitatea PhotoComment, evaluarea mea nu era prezentă în entitatea PhotoRating. Nu am vrut să preiau toate comentariile și să le salvez manual, însă am uitat de metoda .update(). De asemenea, am refactorizat metoda mea pentru a folosi transaction și în loc de RxJS am trecut la promisiuni deoarece, din păcate, RxJS aici nu era cea mai bună opțiune. Așa că codul meu arată acum mai mult sau mai puțin așa:
photos.service.tspublic async ratePhoto(photoId: number, ratePhotoDto: RatePhotoDto, user: User): Promise<void> {
await this.entityManager.transaction(async (entityManager: EntityManager) => {
const photo: Photo = await this.photoRepository.findOne({ where: { photo: { id: photoId } } });
const photoRating: PhotoRating = await this.photoRatingRepository.findOne({ where: { photo: { id: photo.id }, user: { id: user.id } } });
if (!photoRating) {
const newRating: PhotoRating = await entityManager.save( // Saving new rate of photo
PhotoRating,
{ ...ratePhotoDto, user, photo }
);
await entityManager.update( // Fast update all comments that should be updated
PhotoComment,
{ addedBy: { id: user.id }, photo: { id: photo.id } },
{ photoRating: newRating }
);
return;
}
await entityManager.save( // Saving rate of photo
PhotoRating,
{ ...photoRating, ...ratePhotoDto }
);
await entityManager.update( // Fast update all comments that should be updated
PhotoComment,
{ addedBy: { id: user.id }, photo: { id: photo.id } },
{ photoRating }
);
});
}
photo.entity.ts@Entity()
export class Photo { // No changes here
//...
@OneToMany(() => PhotoComment, (photoComment: PhotoComment) => photoComment.photo)
public photoComments: PhotoComment[];
@OneToMany(() => PhotoRating, (photoRating: PhotoRating) => photoRating.photo)
public photoRatings: PhotoRating[];
}
photo-comment.entity.ts@Entity()
export class PhotoComment { // Deleted JoinColumn because I don't need that
//...
@ManyToOne(() => Photo, (photo: Photo) => photo.photoComments, { onDelete: 'CASCADE' })
public photo: Photo;
@ManyToOne(() => PhotoRating, (photoRating: PhotoRating) => photoRating.photoComment)
public photoRating: PhotoRating;
}
photo-rating.entity.ts@Entity()
@Unique(['user', 'photo'])
export class PhotoRating { // Deleted { cascade: true | from PhotoComment because I don't want it
//...
@Transform(({ value }) => +value)
@Column({ type: 'decimal', precision: 3, scale: 2, default: 0 })
public rate: number;
@ManyToOne(() => Photo, (photo: Photo) => photo.photoRatings, { onDelete: 'CASCADE' })
public photo: Photo;
@OneToMany(() => PhotoComment, (photoComment: PhotoComment) => photoComment.photoRating)
public photoComment: PhotoComment;
}
Și acum funcționează perfect și rapid. A trebuit, de asemenea, să ajustez codul meu atunci când utilizatorul adaugă un comentariu când fotografia este deja evaluată, dar este același caz cu metoda .update() ca mai sus
