Search code examples
ormmany-to-manymikro-orm

Mikro-Orm - ManyToMany Relationship how can I delete reference in PivotTable?


I'm using NestJS with mikro-Orm and have a weird behaviour on all of my manyToMany relations.

@ObjectType()
@Entity()
export class Realty {
    @Field(() => ID)
    @PrimaryKey({ columnType: "uuid" })
    id: string = v4();

    @Field(() => [Contact])
    @ManyToMany(() => Contact, (contact) => contact.realties)
    contacts: Collection<Contact>;  
}
@ObjectType()
@Entity()
export class Contact {
    @Field(() => ID)
    @PrimaryKey({ columnType: "uuid" })
    id: string = v4();

    @Field(() => [Realty])
    @ManyToMany(() => Realty, (realty) => realty.contacts, { owner: true })
    realties: Collection<Realty>;
}

When I want to delete a realtyReference from a contact, that works fine and the row from the Contact_Realty PivotTable gets removed. But when I try to delete a contactReference from a realty, nothing happens. Does that only work on the owning side?

ContactsService (works):

async update(updateContactInput: UpdateContactInput) {
        const { id, realtyIds } = updateContactInput;

        const contact = await this.findOneOrFail(id);

        const updated = this.contactsRepository.assign(contact, {
            realties: await this.realtiesService.find(realtyIds),   
        });

        await this.contactsRepository.persistAndFlush(updated);
        return updated;
    }

RealtiesService (returns correct updated entity but doesnt remove row in PivotTable):

async update(updateRealtyGeneralInput: UpdateRealtyGeneralInput) {
        const { id, contactIds } = updateRealtyGeneralInput;

        const realty = await this.realtiesService.findOneOrFail(id);

        const updated = this.realtiesRepository.assign(realty, {
            contacts: await this.contactsService.find(contactIds),
        });

        await this.realtiesRepository.persistAndFlush(updated);

        return updated;
    }

Both return the correct updated entity but only the ContactsService actually removes the row in the pivotTable.

Would really appreciate some help, thanks alot!

I want to remove one or more contacts from a realty and cannot get it to work. Am I doing something wrong?


Solution

  • You always need to have the owning side of your M:N collection initialized/populated, which you apparently don't in the second example. In your case it is Contact.realties, so if you want to manipulate this collection from the inverse side, all the entities you add/remove from the inverse need to have the owning side populated. Only owning side is what is taken into account when computing changesets. I will need to revisit this a bit, we might be able to improve on this thanks to the recent changes like the reference updates added in v5.5.

    Also, there is some misunderstanding in your code. assign mutates the parameter, it does not return "modified entity", it mutates the one you pass in the first argument. If that entity is already managed (as in your case), there is no point in re-persisting it again, just flush.

    async update(updateRealtyGeneralInput: UpdateRealtyGeneralInput) {
      const { id, contactIds } = updateRealtyGeneralInput;
    
      const realty = await this.em.findOneOrFail(Realty, id);
    
      this.realtiesRepository.assign(realty, {
        contacts: await this.em.find(Contact, contactIds, { populate: ['realties'] }),
      });
    
      await this.em.flush(updated);
    
      return realty;
    }