Jonathan
Jonathan

Reputation: 16359

Update a many-to-many relationship with TypeORM

I am having issues updating a entity that has a many-to-many reln, curious if I am doing something wrong, or more specifically what is the right way to do this

Consider the following entities ...

@Entity
class Subject
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  name: string;

  @ManyToMany(() => Note, note => note.subjects)
  @JoinTable()
  notes: Note[];

  ...

@Entity()
export class Note {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @ManyToMany(() => Subject, (subject: Subject) => subject.notes)
  subjects: Subject[];

In my code I find the node and then try to update it and save like so ...

const note = await noteRepo.findOneOrFail(noteId);
const foundSubjects = await subjectRepo.findByIds(Array.from(subjectIds));
note.subjects = foundSubjects;
noteRepo.save(note);

But alas, the subjects are not saved on the note.

What is the right way to do this?

Thanks!

Upvotes: 18

Views: 38764

Answers (5)

Ahmet Emrebas
Ahmet Emrebas

Reputation: 944

Use the save method instead of update.

repo.save({id:1, subjects:[{id:1},{id:2}]})

Upvotes: 0

Cassiano Franco
Cassiano Franco

Reputation: 410

Check this answer: How to update an entity with relations using QueryBuilder in TypeORM

It worked for me.

...
note.subjects = foundSubjects;
const toUpdate = await noteRepo.preload(note)
noteRepo.save(toUpdate);

Don't forget the await, is missing in the answer, or is somthing to do with eager: true... I'm not sure.

Upvotes: 1

Nestor Perez
Nestor Perez

Reputation: 887

In my case Im trying to update an existing relation but that gives me a unique key violation because the relation already exists, so I first need to remove all existing relationships and then add the relationships of my updated user:

export const updateUser = async (user: User): Promise<User | undefined> => {
    /**
     * Get the actual relationships of that user.
     */
    const actualRelationships = await getRepository(User)
        .createQueryBuilder()
        .relation(User, 'roles')
        .of(user).loadMany();

    /**
     * Add new relationships of the user, and delete the old relationships.
     */
    await getRepository(User)
        .createQueryBuilder()
        .relation(User, 'roles')
        .of(user)
        .addAndRemove(user.roles, actualRelationships);

    /**
     * Update only the table USER.
     */
    await getRepository(User)
        .createQueryBuilder()
        .update()
        .set({
            name: user.name,
            username: user.username,
            active: user.active
        })
        .where('id = :id', {id: user.id})
        .execute();

    /**
     * Return the updated user
     */
    return await getUser(user.id, true, true)
};

Upvotes: 17

Sascha
Sascha

Reputation: 1849

By default cascading is set to false, you can enable this as follows:

@Entity()
export class Note {
@PrimaryGeneratedColumn('uuid')
id: string;

@ManyToMany(() => Subject, (subject: Subject) => subject.notes, { cascade: true })
subjects: Subject[];

Upvotes: 5

Jonathan
Jonathan

Reputation: 16359

So the following got it working for me:

  subjectIds.forEach(async id => {
  await connection
    .createQueryBuilder()
    .relation(Note, 'subjects')
    .of(note)
    .add(id);
});

Though, I still kind of feel that the repo.save() method should work

Upvotes: 8

Related Questions