has-many.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __defProps = Object.defineProperties;
  4. var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
  5. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  6. var __hasOwnProp = Object.prototype.hasOwnProperty;
  7. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  8. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  9. var __spreadValues = (a, b) => {
  10. for (var prop in b || (b = {}))
  11. if (__hasOwnProp.call(b, prop))
  12. __defNormalProp(a, prop, b[prop]);
  13. if (__getOwnPropSymbols)
  14. for (var prop of __getOwnPropSymbols(b)) {
  15. if (__propIsEnum.call(b, prop))
  16. __defNormalProp(a, prop, b[prop]);
  17. }
  18. return a;
  19. };
  20. var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
  21. const Utils = require("./../utils");
  22. const Helpers = require("./helpers");
  23. const _ = require("lodash");
  24. const Association = require("./base");
  25. const Op = require("../operators");
  26. class HasMany extends Association {
  27. constructor(source, target, options) {
  28. super(source, target, options);
  29. this.associationType = "HasMany";
  30. this.targetAssociation = null;
  31. this.sequelize = source.sequelize;
  32. this.isMultiAssociation = true;
  33. this.foreignKeyAttribute = {};
  34. if (this.options.through) {
  35. throw new Error("N:M associations are not supported with hasMany. Use belongsToMany instead");
  36. }
  37. if (this.isSelfAssociation) {
  38. this.targetAssociation = this;
  39. }
  40. if (this.as) {
  41. this.isAliased = true;
  42. if (_.isPlainObject(this.as)) {
  43. this.options.name = this.as;
  44. this.as = this.as.plural;
  45. } else {
  46. this.options.name = {
  47. plural: this.as,
  48. singular: Utils.singularize(this.as)
  49. };
  50. }
  51. } else {
  52. this.as = this.target.options.name.plural;
  53. this.options.name = this.target.options.name;
  54. }
  55. if (_.isObject(this.options.foreignKey)) {
  56. this.foreignKeyAttribute = this.options.foreignKey;
  57. this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName;
  58. } else if (this.options.foreignKey) {
  59. this.foreignKey = this.options.foreignKey;
  60. }
  61. if (!this.foreignKey) {
  62. this.foreignKey = Utils.camelize([
  63. this.source.options.name.singular,
  64. this.source.primaryKeyAttribute
  65. ].join("_"));
  66. }
  67. if (this.target.rawAttributes[this.foreignKey]) {
  68. this.identifierField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey;
  69. this.foreignKeyField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey;
  70. }
  71. this.sourceKey = this.options.sourceKey || this.source.primaryKeyAttribute;
  72. if (this.source.rawAttributes[this.sourceKey]) {
  73. this.sourceKeyAttribute = this.sourceKey;
  74. this.sourceKeyField = this.source.rawAttributes[this.sourceKey].field || this.sourceKey;
  75. } else {
  76. this.sourceKeyAttribute = this.source.primaryKeyAttribute;
  77. this.sourceKeyField = this.source.primaryKeyField;
  78. }
  79. const plural = _.upperFirst(this.options.name.plural);
  80. const singular = _.upperFirst(this.options.name.singular);
  81. this.associationAccessor = this.as;
  82. this.accessors = {
  83. get: `get${plural}`,
  84. set: `set${plural}`,
  85. addMultiple: `add${plural}`,
  86. add: `add${singular}`,
  87. create: `create${singular}`,
  88. remove: `remove${singular}`,
  89. removeMultiple: `remove${plural}`,
  90. hasSingle: `has${singular}`,
  91. hasAll: `has${plural}`,
  92. count: `count${plural}`
  93. };
  94. }
  95. _injectAttributes() {
  96. const newAttributes = {
  97. [this.foreignKey]: __spreadValues({
  98. type: this.options.keyType || this.source.rawAttributes[this.sourceKeyAttribute].type,
  99. allowNull: true
  100. }, this.foreignKeyAttribute)
  101. };
  102. const constraintOptions = __spreadValues({}, this.options);
  103. if (this.options.constraints !== false) {
  104. const target = this.target.rawAttributes[this.foreignKey] || newAttributes[this.foreignKey];
  105. constraintOptions.onDelete = constraintOptions.onDelete || (target.allowNull ? "SET NULL" : "CASCADE");
  106. constraintOptions.onUpdate = constraintOptions.onUpdate || "CASCADE";
  107. }
  108. Helpers.addForeignKeyConstraints(newAttributes[this.foreignKey], this.source, this.target, constraintOptions, this.sourceKeyField);
  109. Utils.mergeDefaults(this.target.rawAttributes, newAttributes);
  110. this.target.refreshAttributes();
  111. this.source.refreshAttributes();
  112. this.identifierField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey;
  113. this.foreignKeyField = this.target.rawAttributes[this.foreignKey].field || this.foreignKey;
  114. this.sourceKeyField = this.source.rawAttributes[this.sourceKey].field || this.sourceKey;
  115. Helpers.checkNamingCollision(this);
  116. return this;
  117. }
  118. mixin(obj) {
  119. const methods = ["get", "count", "hasSingle", "hasAll", "set", "add", "addMultiple", "remove", "removeMultiple", "create"];
  120. const aliases = {
  121. hasSingle: "has",
  122. hasAll: "has",
  123. addMultiple: "add",
  124. removeMultiple: "remove"
  125. };
  126. Helpers.mixinMethods(this, obj, methods, aliases);
  127. }
  128. async get(instances, options = {}) {
  129. const where = {};
  130. let Model = this.target;
  131. let instance;
  132. let values;
  133. if (!Array.isArray(instances)) {
  134. instance = instances;
  135. instances = void 0;
  136. }
  137. options = __spreadValues({}, options);
  138. if (this.scope) {
  139. Object.assign(where, this.scope);
  140. }
  141. if (instances) {
  142. values = instances.map((_instance) => _instance.get(this.sourceKey, { raw: true }));
  143. if (options.limit && instances.length > 1) {
  144. options.groupedLimit = {
  145. limit: options.limit,
  146. on: this,
  147. values
  148. };
  149. delete options.limit;
  150. } else {
  151. where[this.foreignKey] = {
  152. [Op.in]: values
  153. };
  154. delete options.groupedLimit;
  155. }
  156. } else {
  157. where[this.foreignKey] = instance.get(this.sourceKey, { raw: true });
  158. }
  159. options.where = options.where ? { [Op.and]: [where, options.where] } : where;
  160. if (Object.prototype.hasOwnProperty.call(options, "scope")) {
  161. if (!options.scope) {
  162. Model = Model.unscoped();
  163. } else {
  164. Model = Model.scope(options.scope);
  165. }
  166. }
  167. if (Object.prototype.hasOwnProperty.call(options, "schema")) {
  168. Model = Model.schema(options.schema, options.schemaDelimiter);
  169. }
  170. const results = await Model.findAll(options);
  171. if (instance)
  172. return results;
  173. const result = {};
  174. for (const _instance of instances) {
  175. result[_instance.get(this.sourceKey, { raw: true })] = [];
  176. }
  177. for (const _instance of results) {
  178. result[_instance.get(this.foreignKey, { raw: true })].push(_instance);
  179. }
  180. return result;
  181. }
  182. async count(instance, options) {
  183. options = Utils.cloneDeep(options);
  184. options.attributes = [
  185. [
  186. this.sequelize.fn("COUNT", this.sequelize.col(`${this.target.name}.${this.target.primaryKeyField}`)),
  187. "count"
  188. ]
  189. ];
  190. options.raw = true;
  191. options.plain = true;
  192. const result = await this.get(instance, options);
  193. return parseInt(result.count, 10);
  194. }
  195. async has(sourceInstance, targetInstances, options) {
  196. const where = {};
  197. if (!Array.isArray(targetInstances)) {
  198. targetInstances = [targetInstances];
  199. }
  200. options = __spreadProps(__spreadValues({}, options), {
  201. scope: false,
  202. attributes: [this.target.primaryKeyAttribute],
  203. raw: true
  204. });
  205. where[Op.or] = targetInstances.map((instance) => {
  206. if (instance instanceof this.target) {
  207. return instance.where();
  208. }
  209. return {
  210. [this.target.primaryKeyAttribute]: instance
  211. };
  212. });
  213. options.where = {
  214. [Op.and]: [
  215. where,
  216. options.where
  217. ]
  218. };
  219. const associatedObjects = await this.get(sourceInstance, options);
  220. return associatedObjects.length === targetInstances.length;
  221. }
  222. async set(sourceInstance, targetInstances, options) {
  223. if (targetInstances === null) {
  224. targetInstances = [];
  225. } else {
  226. targetInstances = this.toInstanceArray(targetInstances);
  227. }
  228. const oldAssociations = await this.get(sourceInstance, __spreadProps(__spreadValues({}, options), { scope: false, raw: true }));
  229. const promises = [];
  230. const obsoleteAssociations = oldAssociations.filter((old) => !targetInstances.find((obj) => obj[this.target.primaryKeyAttribute] === old[this.target.primaryKeyAttribute]));
  231. const unassociatedObjects = targetInstances.filter((obj) => !oldAssociations.find((old) => obj[this.target.primaryKeyAttribute] === old[this.target.primaryKeyAttribute]));
  232. let updateWhere;
  233. let update;
  234. if (obsoleteAssociations.length > 0) {
  235. update = {};
  236. update[this.foreignKey] = null;
  237. updateWhere = {
  238. [this.target.primaryKeyAttribute]: obsoleteAssociations.map((associatedObject) => associatedObject[this.target.primaryKeyAttribute])
  239. };
  240. promises.push(this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), {
  241. where: updateWhere
  242. })));
  243. }
  244. if (unassociatedObjects.length > 0) {
  245. updateWhere = {};
  246. update = {};
  247. update[this.foreignKey] = sourceInstance.get(this.sourceKey);
  248. Object.assign(update, this.scope);
  249. updateWhere[this.target.primaryKeyAttribute] = unassociatedObjects.map((unassociatedObject) => unassociatedObject[this.target.primaryKeyAttribute]);
  250. promises.push(this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), {
  251. where: updateWhere
  252. })));
  253. }
  254. await Promise.all(promises);
  255. return sourceInstance;
  256. }
  257. async add(sourceInstance, targetInstances, options = {}) {
  258. if (!targetInstances)
  259. return Promise.resolve();
  260. targetInstances = this.toInstanceArray(targetInstances);
  261. const update = __spreadValues({
  262. [this.foreignKey]: sourceInstance.get(this.sourceKey)
  263. }, this.scope);
  264. const where = {
  265. [this.target.primaryKeyAttribute]: targetInstances.map((unassociatedObject) => unassociatedObject.get(this.target.primaryKeyAttribute))
  266. };
  267. await this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), { where }));
  268. return sourceInstance;
  269. }
  270. async remove(sourceInstance, targetInstances, options = {}) {
  271. const update = {
  272. [this.foreignKey]: null
  273. };
  274. targetInstances = this.toInstanceArray(targetInstances);
  275. const where = {
  276. [this.foreignKey]: sourceInstance.get(this.sourceKey),
  277. [this.target.primaryKeyAttribute]: targetInstances.map((targetInstance) => targetInstance.get(this.target.primaryKeyAttribute))
  278. };
  279. await this.target.unscoped().update(update, __spreadProps(__spreadValues({}, options), { where }));
  280. return this;
  281. }
  282. async create(sourceInstance, values, options = {}) {
  283. if (Array.isArray(options)) {
  284. options = {
  285. fields: options
  286. };
  287. }
  288. if (values === void 0) {
  289. values = {};
  290. }
  291. if (this.scope) {
  292. for (const attribute of Object.keys(this.scope)) {
  293. values[attribute] = this.scope[attribute];
  294. if (options.fields)
  295. options.fields.push(attribute);
  296. }
  297. }
  298. values[this.foreignKey] = sourceInstance.get(this.sourceKey);
  299. if (options.fields)
  300. options.fields.push(this.foreignKey);
  301. return await this.target.create(values, options);
  302. }
  303. verifyAssociationAlias(alias) {
  304. if (typeof alias === "string") {
  305. return this.as === alias;
  306. }
  307. if (alias && alias.plural) {
  308. return this.as === alias.plural;
  309. }
  310. return !this.isAliased;
  311. }
  312. }
  313. module.exports = HasMany;
  314. module.exports.HasMany = HasMany;
  315. module.exports.default = HasMany;
  316. //# sourceMappingURL=has-many.js.map