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.ts
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;

                    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.ts
public 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