One-to-Many polymorphic association with sequelize in NestJS

20 Views Asked by At

How to do One-to-Many polymorphic association with Sequelized, as described in the Sequelize manual here in NestJS?

I have a project written in vanilla node + express that uses the solution described in the sequelize link above and works perfectly well. This projects has "users" and "organisations", both having many "contacts".

In the vanilla case, "contact" has the following model:

Contact.init(
      {
        type: {
          type: DataTypes.ENUM,
          values: ["email", "phone", "mobile", "postal"],
          allowNull: false,
        },
        value: {
          type: DataTypes.STRING,
          allowNull: false,
        },
        contactableId: DataTypes.INTEGER,
        contactableType: DataTypes.STRING
      },
      {
        sequelize,
        tableName: "contacts",
        modelName: "contact",
      }
    );

and it is associated to the "user" and "org" models as follows:

setAssociations: () => {
    User.hasMany(Contact, { 
      foreignKey: 'contactableId',
      constraints: false,
      scope: {
        contactableType: 'user'
      }
    });
    Contact.belongsTo(User, {
      foreignKey: 'contactableId',
      constraints: false,
      onDelete: "CASCADE"
    });

    Organisation.Contacts = Organisation.hasMany(Contact, {
      foreignKey: 'contactableId',
      constraints: false,
      scope: {
        contactableType: 'org'
      }
    });
    Contact.Organisation = Contact.belongsTo(Organisation, {
      foreignKey: 'contactableId',
      constraints: false,
      onDelete: "CASCADE"
    });
  },

For NestJS, I have modeled trying to follow the same principles as described in the sequelize explanation:

  • User

    @Table( { modelName: "user", tableName: "users" }) class User extends Model {

        @PrimaryKey
        @Column
        uid: number;
    
        ...
    
        @HasMany(() => Contact, "contactableId")
        contacts: Contact[];
      }
    
  • Org:

    @Table( { modelName: "organisation", tableName: "organisations" }) class Organisation extends Model {

    @PrimaryKey
    @Column
    oid: number;
    
    ...
    
    @HasMany(() => Contact, "contactableId")
    contacts: Contact[]
    

    }

  • and Contact:

    @Table({ modelName: "contact", tableName: "contacts" }) class Contact extends Model {

    @PrimaryKey
    @Column
    cid!: number;
    
    @Column
    contactableId: number;
    
    @Column
    contactableType: string;
    
    ...
    
    getContactable(options) {
      if (!this.contactableType) return Promise.resolve(null);
      const mixinMethodName = `get${uppercaseFirst(this.contactableType)}`;
      return this[mixinMethodName](options);
    }
    
    @AfterFind
    static FindResult(findResult) {
      if (!Array.isArray(findResult)) findResult = [findResult]; 
      for (const instance of findResult) {
        if (instance.contactableType === "user" && instance.user !== undefined) {
          instance.contactable = instance.user;
        } else if (instance.contactableType === "org" && instance.organisation !== undefined) {
          instance.contactable = instance.organisation;
        }
        // To prevent mistakes:
        delete instance.user;
        delete instance.dataValues.user;
        delete instance.organisation;
        delete instance.dataValues.organisation;
      }
    }
    

    }

However, I cannot see how to do the "scope" parts in setAssociations for vanilla case above. How can I do it to get the value of contactableType set to "user" when the contact is for a user, and set to "org" when the contact is for a organisation?

0

There are 0 best solutions below