* Associate one or several rows with source instance. It will not un-associate any already associated instance * that may be missing from `newInstances`. * * @param {Model} sourceInstance source instance to associate new instances with * @param {Model|Model[]|string[]|string|number[]|num
(sourceInstance, newInstances, options)
| 670 | * @returns {Promise} |
| 671 | */ |
| 672 | async add(sourceInstance, newInstances, options) { |
| 673 | // If newInstances is null or undefined, no-op |
| 674 | if (!newInstances) return Promise.resolve(); |
| 675 | |
| 676 | options = { ...options }; |
| 677 | |
| 678 | const association = this; |
| 679 | const sourceKey = association.sourceKey; |
| 680 | const targetKey = association.targetKey; |
| 681 | const identifier = association.identifier; |
| 682 | const foreignIdentifier = association.foreignIdentifier; |
| 683 | const defaultAttributes = options.through || {}; |
| 684 | |
| 685 | newInstances = association.toInstanceArray(newInstances); |
| 686 | |
| 687 | const where = { |
| 688 | [identifier]: sourceInstance.get(sourceKey), |
| 689 | [foreignIdentifier]: newInstances.map(newInstance => newInstance.get(targetKey)), |
| 690 | ...association.through.scope |
| 691 | }; |
| 692 | |
| 693 | const updateAssociations = currentRows => { |
| 694 | const promises = []; |
| 695 | const unassociatedObjects = []; |
| 696 | const changedAssociations = []; |
| 697 | for (const obj of newInstances) { |
| 698 | const existingAssociation = currentRows && currentRows.find(current => current[foreignIdentifier] === obj.get(targetKey)); |
| 699 | |
| 700 | if (!existingAssociation) { |
| 701 | unassociatedObjects.push(obj); |
| 702 | } else { |
| 703 | const throughAttributes = obj[association.through.model.name]; |
| 704 | const attributes = { ...defaultAttributes, ...throughAttributes }; |
| 705 | |
| 706 | if (Object.keys(attributes).some(attribute => attributes[attribute] !== existingAssociation[attribute])) { |
| 707 | changedAssociations.push(obj); |
| 708 | } |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | if (unassociatedObjects.length > 0) { |
| 713 | const bulk = unassociatedObjects.map(unassociatedObject => { |
| 714 | const throughAttributes = unassociatedObject[association.through.model.name]; |
| 715 | const attributes = { ...defaultAttributes, ...throughAttributes }; |
| 716 | |
| 717 | attributes[identifier] = sourceInstance.get(sourceKey); |
| 718 | attributes[foreignIdentifier] = unassociatedObject.get(targetKey); |
| 719 | |
| 720 | Object.assign(attributes, association.through.scope); |
| 721 | |
| 722 | return attributes; |
| 723 | }); |
| 724 | |
| 725 | promises.push(association.through.model.bulkCreate(bulk, { validate: true, ...options })); |
| 726 | } |
| 727 | |
| 728 | for (const assoc of changedAssociations) { |
| 729 | let throughAttributes = assoc[association.through.model.name]; |
nothing calls this directly
no test coverage detected