var Utils = require('./../utils') , Transaction = require('./../transaction') module.exports = (function() { var HasManyDoubleLinked = function(association, instance) { this.association = association this.instance = instance // Alias the quoting methods for code brevity this.QueryInterface = instance.QueryInterface } HasManyDoubleLinked.prototype.injectGetter = function(options) { var self = this , _options = options , smart var customEventEmitter = new Utils.CustomEventEmitter(function() { var where = {} , through = self.association.through , options = _options || {} , queryOptions = {} , targetAssociation = self.association.targetAssociation //fully qualify var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys) , instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id' , foreignPrimary = Object.keys(self.association.target.primaryKeys) foreignPrimary = foreignPrimary.length === 1 ? foreignPrimary[0] : 'id' where[through.tableName+"."+self.association.identifier] = self.instance[instancePrimaryKey] where[through.tableName+"."+self.association.foreignIdentifier] = {join: self.association.target.tableName+"."+foreignPrimary} if (Object(targetAssociation.through) === targetAssociation.through) { queryOptions.hasJoinTableModel = true queryOptions.joinTableModel = through if (!options.attributes) { options.attributes = [ self.QueryInterface.quoteIdentifier(self.association.target.tableName)+".*" ] } if (options.joinTableAttributes) { options.joinTableAttributes.forEach(function (elem) { options.attributes.push( self.QueryInterface.quoteIdentifiers(through.tableName + '.' + elem) + ' as ' + self.QueryInterface.quoteIdentifier(through.name + '.' + elem, true) ) }) } else { Utils._.forOwn(through.rawAttributes, function (elem, key) { options.attributes.push( self.QueryInterface.quoteIdentifiers(through.tableName + '.' + key) + ' as ' + self.QueryInterface.quoteIdentifier(through.name + '.' + key, true) ) }) } } if (options.where) { if (Array.isArray(options.where)) { smart = Utils.smartWhere([where, options.where], self.association.target.daoFactoryManager.sequelize.options.dialect) smart = Utils.compileSmartWhere.call(self.association.target, smart, self.association.target.daoFactoryManager.sequelize.options.dialect) if (smart.length > 0) { options.where = smart } } else { smart = Utils.smartWhere([where, options.where], self.association.target.daoFactoryManager.sequelize.options.dialect) smart = Utils.compileSmartWhere.call(self.association.target, smart, self.association.target.daoFactoryManager.sequelize.options.dialect) if (smart.length > 0) { options.where = smart } } } else { options.where = where; } self.association.target.findAllJoin(through.tableName, options, queryOptions) .on('success', function(objects) { customEventEmitter.emit('success', objects) }) .on('error', function(err){ customEventEmitter.emit('error', err) }) .on('sql', function(sql) { customEventEmitter.emit('sql', sql)}) }) return customEventEmitter.run() } HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations, defaultAttributes) { var self = this , chainer = new Utils.QueryChainer() , targetAssociation = self.association.targetAssociation , foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier , sourceKeys = Object.keys(self.association.source.primaryKeys) , targetKeys = Object.keys(self.association.target.primaryKeys) , obsoleteAssociations = [] , changedAssociations = [] , options = {} , unassociatedObjects; if ((defaultAttributes || {}).transaction instanceof Transaction) { options.transaction = defaultAttributes.transaction delete defaultAttributes.transaction } unassociatedObjects = newAssociations.filter(function (obj) { return !Utils._.find(oldAssociations, function (old) { return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id) }) }) oldAssociations.forEach(function (old) { var newObj = Utils._.find(newAssociations, function (obj) { return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id) }) if (!newObj) { obsoleteAssociations.push(old) } else if (Object(targetAssociation.through) === targetAssociation.through) { var throughAttributes = newObj[self.association.through.name]; // Quick-fix for subtle bug when using existing objects that might have the through model attached (not as an attribute object) if (throughAttributes instanceof self.association.through.DAO) { throughAttributes = {}; } var changedAssociation = { where: {}, attributes: Utils._.defaults({}, throughAttributes, defaultAttributes) } changedAssociation.where[self.association.identifier] = self.instance[self.association.identifier] || self.instance[self.association.source.primaryKeyAttributes[0]] || self.instance.id changedAssociation.where[foreignIdentifier] = newObj[foreignIdentifier] || newObj[self.association.target.primaryKeyAttributes[0]] || newObj.id if (Object.keys(changedAssociation.attributes).length) { changedAssociations.push(changedAssociation) } } }) if (obsoleteAssociations.length > 0) { var foreignIds = obsoleteAssociations.map(function (associatedObject) { return ((targetKeys.length === 1) ? associatedObject[targetKeys[0]] : associatedObject.id) }) var where = {} where[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) where[foreignIdentifier] = foreignIds chainer.add(self.association.through.destroy(where, options)) } if (unassociatedObjects.length > 0) { var bulk = unassociatedObjects.map(function(unassociatedObject) { var attributes = {} attributes[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id) attributes[foreignIdentifier] = ((targetKeys.length === 1) ? unassociatedObject[targetKeys[0]] : unassociatedObject.id) if (Object(targetAssociation.through) === targetAssociation.through) { attributes = Utils._.defaults(attributes, unassociatedObject[targetAssociation.through.name], defaultAttributes) } return attributes }) chainer.add(self.association.through.bulkCreate(bulk, options)) } if (changedAssociations.length > 0) { changedAssociations.forEach(function (assoc) { chainer.add(self.association.through.update(assoc.attributes, assoc.where, options)) }) } chainer .run() .success(function() { emitterProxy.emit('success', newAssociations) }) .error(function(err) { emitterProxy.emit('error', err) }) .on('sql', function(sql) { emitterProxy.emit('sql', sql) }) } HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) { var attributes = {} , targetAssociation = this.association.targetAssociation , foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier , options = {} var sourceKeys = Object.keys(this.association.source.primaryKeys); var targetKeys = Object.keys(this.association.target.primaryKeys); if ((additionalAttributes || {}).transaction instanceof Transaction) { options.transaction = additionalAttributes.transaction delete additionalAttributes.transaction } attributes[this.association.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id) attributes[foreignIdentifier] = ((targetKeys.length === 1) ? newAssociation[targetKeys[0]] : newAssociation.id) if (exists) { var where = attributes attributes = Utils._.defaults({}, newAssociation[targetAssociation.through.name], additionalAttributes) if (Object.keys(attributes).length) { targetAssociation.through.update(attributes, where).proxy(emitterProxy) } else { emitterProxy.emit('success') } } else { attributes = Utils._.defaults(attributes, newAssociation[targetAssociation.through.name], additionalAttributes) this.association.through.create(attributes, options) .success(function() { emitterProxy.emit('success', newAssociation) }) .error(function(err) { emitterProxy.emit('error', err) }) .on('sql', function(sql) { emitterProxy.emit('sql', sql) }) } } return HasManyDoubleLinked })()