mixin.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. 'use strict';
  2. const _ = require('lodash');
  3. const HasOne = require('./has-one');
  4. const HasMany = require('./has-many');
  5. const BelongsToMany = require('./belongs-to-many');
  6. const BelongsTo = require('./belongs-to');
  7. function isModel(model, sequelize) {
  8. return model
  9. && model.prototype
  10. && model.prototype instanceof sequelize.Sequelize.Model;
  11. }
  12. const Mixin = {
  13. hasMany(target, options = {}) {
  14. if (!isModel(target, this.sequelize)) {
  15. throw new Error(`${this.name}.hasMany called with something that's not a subclass of Sequelize.Model`);
  16. }
  17. const source = this;
  18. // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
  19. options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
  20. options.useHooks = options.hooks;
  21. Object.assign(options, _.omit(source.options, ['hooks']));
  22. if (options.useHooks) {
  23. this.runHooks('beforeAssociate', { source, target, type: HasMany }, options);
  24. }
  25. // the id is in the foreign table or in a connecting table
  26. const association = new HasMany(source, target, options);
  27. source.associations[association.associationAccessor] = association;
  28. association._injectAttributes();
  29. association.mixin(source.prototype);
  30. if (options.useHooks) {
  31. this.runHooks('afterAssociate', { source, target, type: HasMany, association }, options);
  32. }
  33. return association;
  34. },
  35. belongsToMany(target, options = {}) {
  36. if (!isModel(target, this.sequelize)) {
  37. throw new Error(`${this.name}.belongsToMany called with something that's not a subclass of Sequelize.Model`);
  38. }
  39. const source = this;
  40. // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
  41. options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
  42. options.useHooks = options.hooks;
  43. options.timestamps = options.timestamps === undefined ? this.sequelize.options.timestamps : options.timestamps;
  44. Object.assign(options, _.omit(source.options, ['hooks', 'timestamps', 'scopes', 'defaultScope']));
  45. if (options.useHooks) {
  46. this.runHooks('beforeAssociate', { source, target, type: BelongsToMany }, options);
  47. }
  48. // the id is in the foreign table or in a connecting table
  49. const association = new BelongsToMany(source, target, options);
  50. source.associations[association.associationAccessor] = association;
  51. association._injectAttributes();
  52. association.mixin(source.prototype);
  53. if (options.useHooks) {
  54. this.runHooks('afterAssociate', { source, target, type: BelongsToMany, association }, options);
  55. }
  56. return association;
  57. },
  58. getAssociations(target) {
  59. return Object.values(this.associations).filter(association => association.target.name === target.name);
  60. },
  61. getAssociationForAlias(target, alias) {
  62. // Two associations cannot have the same alias, so we can use find instead of filter
  63. return this.getAssociations(target).find(association => association.verifyAssociationAlias(alias)) || null;
  64. }
  65. };
  66. // The logic for hasOne and belongsTo is exactly the same
  67. function singleLinked(Type) {
  68. return function(target, options = {}) {
  69. // eslint-disable-next-line no-invalid-this
  70. const source = this;
  71. if (!isModel(target, source.sequelize)) {
  72. throw new Error(`${source.name}.${_.lowerFirst(Type.name)} called with something that's not a subclass of Sequelize.Model`);
  73. }
  74. // Since this is a mixin, we'll need a unique letiable name for hooks (since Model will override our hooks option)
  75. options.hooks = options.hooks === undefined ? false : Boolean(options.hooks);
  76. options.useHooks = options.hooks;
  77. if (options.useHooks) {
  78. source.runHooks('beforeAssociate', { source, target, type: Type }, options);
  79. }
  80. // the id is in the foreign table
  81. const association = new Type(source, target, Object.assign(options, source.options));
  82. source.associations[association.associationAccessor] = association;
  83. association._injectAttributes();
  84. association.mixin(source.prototype);
  85. if (options.useHooks) {
  86. source.runHooks('afterAssociate', { source, target, type: Type, association }, options);
  87. }
  88. return association;
  89. };
  90. }
  91. Mixin.hasOne = singleLinked(HasOne);
  92. Mixin.belongsTo = singleLinked(BelongsTo);
  93. module.exports = Mixin;
  94. module.exports.Mixin = Mixin;
  95. module.exports.default = Mixin;