feathersjs-ecosystem / feathers-objection

Feathers database adapter for Objection.js, an ORM based on KnexJS SQL query builder for Postgres, Redshift, MSSQL, MySQL, MariaDB, SQLite3, and Oracle. Forked from feathers-knex.
MIT License
98 stars 49 forks source link

Add an association (manyToMany) #139

Open Looped8 opened 3 years ago

Looped8 commented 3 years ago

Hi, I have 2 models, User and Department, with a third model (UserDepartment) being the join table of both in a many to many relation.

I am able to add a new Department to an User but Im not able to assign an already existing Department to an User.

UserModel:

/**
 * users model
 * @extends Model
 */
class UserModel extends Model {
  createdAt!: Date;
  updatedAt!: Date;

  /**
   * @override
   */
  static get tableName() {
    return 'users';
  }

  /**
   * @override
   */
  static get jsonSchema() {
    return {
      type: 'object',
      required: ['email','firstname','lastname','password',],
      search: ['email','firstname','lastname','cellphoneNumber','officeNumber','extensionNumber',],
      properties: {
        id: {
          type: 'integer'
        },
        email: {
          type: 'string'
        },
        firstname: {
          type: 'string'
        },
        lastname: {
          type: 'string'
        },
        cellphoneNumber: {
          type: 'string'
        },
        officeNumber: {
          type: 'string'
        },
        extensionNumber: {
          type: 'string'
        },
        password: {
          type: 'string'
        },
        isMaster: {
          type: 'boolean'
        },
        removed: {
          type: 'boolean'
        },
        createdAt: {
          type: 'date-time'
        },
        updatedAt: {
          type: 'date-time'
        },
        companyId: {
          type: 'integer'
        },
        usersGroupId: {
          type: 'integer'
        },
        avatarId: {
          type: 'integer'
        },
      }
    };
  }

  /**
   * @override
   */
  static get relationMappings() {
    return {
      company: {
        relation: Model.BelongsToOneRelation,
        modelClass: CompanyModel(),
        join: {
          from: 'users.companyId',
          to: 'companies.id'
        }
      },
      userGroup: {
        relation: Model.BelongsToOneRelation,
        modelClass: UserGroupModel(),
        join: {
          from: 'users.usersGroupId',
          to: 'users_groups.id'
        }
      },
      departments: {
        relation: Model.ManyToManyRelation,
        modelClass: DepartmentModel(),
        join: {
          from: 'users.id',
          through: {
            from: 'users_departments.userId',
            to: 'users_departments.departmentId',
            extra: ['canAssignAny'],
          },
          to: 'departments.id'
        }
      },
    };
  }

  $beforeInsert(): void {
    this.createdAt = this.updatedAt = new Date();
  }

  $beforeUpdate(): void {
    this.updatedAt = new Date();
  }

}`

DepartmentModel:

/**
 * departments model
 * @extends Model
 */
class DepartmentModel extends Model {
  createdAt!: Date;
  updatedAt!: Date;

  /**
   * @override
   */
  static get tableName() {
    return 'departments';
  }

  /**
   * @override
   */
  static get jsonSchema() {
    return {
      type: 'object',
      required: ['path',],
      search: ['path',],
      properties: {
        id: {
          type: 'integer'
        },
        path: {
          type: 'string'
        },
        removed: {
          type: 'boolean'
        },
        createdAt: {
          type: 'date-time',
          default: new Date(),
        },
        updatedAt: {
          type: 'date-time',
          default: new Date(),
        },
        companyId: {
          type: 'integer'
        },
      }
    };
  }

  /**
   * @override
   */
  static get relationMappings() {
    return {
      company: {
        relation: Model.BelongsToOneRelation,
        modelClass: CompanyModel(),
        join: {
          from: 'departments.companyId',
          to: 'companies.id'
        }
      },
      users: {
        relation: Model.ManyToManyRelation,
        modelClass: UserModel(),
        join: {
          from: 'departments.id',
          through: {
            from: 'users_departments.departmentId',
            to: 'users_departments.userId',
            extra: ['canAssignAny'],
          },
          to: 'users.id'
        }
      },
    };
  }

  $beforeInsert(): void {
    this.createdAt = this.updatedAt = new Date();
  }

  $beforeUpdate(): void {
    this.updatedAt = new Date();
  }

UserDepartmentModel:

/**
 * users_departments model
 * @extends Model
 */
class UserDepartmentModel extends Model {
  createdAt!: Date;
  updatedAt!: Date;

  /**
   * @override
   */
  static get tableName() {
    return 'users_departments';
  }

  /**
   * @override
   */
  static get jsonSchema() {
    return {
      type: 'object',
      required: ['userId','departmentId',],
      search: [],
      properties: {
        canAssignAny: {
          type: 'boolean'
        },
        createdAt: {
          type: 'date-time',
          default: new Date(),
        },
        updatedAt: {
          type: 'date-time',
          default: new Date(),
        },
        userId: {
          type: 'integer'
        },
        departmentId: {
          type: 'integer'
        },
      }
    };
  }

  /**
   * @override
   */
  static get relationMappings() {
    return {
      user: {
        relation: Model.BelongsToOneRelation,
        modelClass: UserModel(),
        join: {
          from: 'users_departments.userId',
          to: 'users.id'
        }
      },
      department: {
        relation: Model.BelongsToOneRelation,
        modelClass: DepartmentModel(),
        join: {
          from: 'users_departments.departmentId',
          to: 'departments.id',
        }
      },
    };
  }

  $beforeInsert(): void {
    throw new Error ('caca');
    this.createdAt = this.updatedAt = new Date();
  }

  $beforeUpdate(): void {
    this.updatedAt = new Date();
  }

This is what I do to create a new department on the fly and add it to an User:

const test1 = await app.service('users').patch(
    3, {
      departments: [
        {
          path: '/zazeaezeaze',
          companyId: 2,
          canAssignAny: true
        },
      ],
    });

It returns the User fields with an array departments containing the newly created Department

And this is what Im tryin to do to add an existing Department:

const test = await app.service('users').patch(
    3, {
      departments: [
        {
          id: 2,
          canAssignAny: true
        },
      ],
    });

Which fails to this:

type: 'NotFound',
  data: {
    message: 'model (id=2) is not a child of model (id=3). If you want to relate it, use the relate option. If you want to insert it with an id, use the insertMissing option',
    dataPath: [ 'departments', 0 ]
  },

Im certainly doing something wrong but cant figure out what. Thanks.

zanzara commented 2 years ago

I don't know, but it seems to me, that this repo is kinda dead. I have also issues with manytoMany Asso. (posted it in discussion). There are really lacking some "real-world" full-examples of best-practice done with feathers/Objection and relations.

Or is there another, better place to ask for? (forum?)

dekelev commented 2 years ago

You can check the tests for more examples. Also, a lot of the info is in Objection website, so you search examples there as well.

I'm not using Feathers for almost a year now, which makes it difficult to continue and working on this project, hence it will progress with PR for the most part in order to add new features.

The project has an extensive test coverage and is used in production within many companies, so it is considers to be very stable.