has-many-double-linked.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. var Utils = require('./../utils')
  2. , Transaction = require('./../transaction')
  3. module.exports = (function() {
  4. var HasManyDoubleLinked = function(association, instance) {
  5. this.association = association
  6. this.instance = instance
  7. // Alias the quoting methods for code brevity
  8. this.QueryInterface = instance.QueryInterface
  9. }
  10. HasManyDoubleLinked.prototype.injectGetter = function(options) {
  11. var self = this
  12. , _options = options
  13. , smart
  14. var customEventEmitter = new Utils.CustomEventEmitter(function() {
  15. var where = {}
  16. , through = self.association.through
  17. , options = _options || {}
  18. , queryOptions = {}
  19. , targetAssociation = self.association.targetAssociation
  20. //fully qualify
  21. var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys)
  22. , instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id'
  23. , foreignPrimary = Object.keys(self.association.target.primaryKeys)
  24. foreignPrimary = foreignPrimary.length === 1 ? foreignPrimary[0] : 'id'
  25. where[through.tableName+"."+self.association.identifier] = self.instance[instancePrimaryKey]
  26. where[through.tableName+"."+self.association.foreignIdentifier] = {join: self.association.target.tableName+"."+foreignPrimary}
  27. if (Object(targetAssociation.through) === targetAssociation.through) {
  28. queryOptions.hasJoinTableModel = true
  29. queryOptions.joinTableModel = through
  30. if (!options.attributes) {
  31. options.attributes = [
  32. self.QueryInterface.quoteIdentifier(self.association.target.tableName)+".*"
  33. ]
  34. }
  35. if (options.joinTableAttributes) {
  36. options.joinTableAttributes.forEach(function (elem) {
  37. options.attributes.push(
  38. self.QueryInterface.quoteIdentifiers(through.tableName + '.' + elem) + ' as ' +
  39. self.QueryInterface.quoteIdentifier(through.name + '.' + elem, true)
  40. )
  41. })
  42. } else {
  43. Utils._.forOwn(through.rawAttributes, function (elem, key) {
  44. options.attributes.push(
  45. self.QueryInterface.quoteIdentifiers(through.tableName + '.' + key) + ' as ' +
  46. self.QueryInterface.quoteIdentifier(through.name + '.' + key, true)
  47. )
  48. })
  49. }
  50. }
  51. if (options.where) {
  52. if (Array.isArray(options.where)) {
  53. smart = Utils.smartWhere([where, options.where], self.association.target.daoFactoryManager.sequelize.options.dialect)
  54. smart = Utils.compileSmartWhere.call(self.association.target, smart, self.association.target.daoFactoryManager.sequelize.options.dialect)
  55. if (smart.length > 0) {
  56. options.where = smart
  57. }
  58. } else {
  59. smart = Utils.smartWhere([where, options.where], self.association.target.daoFactoryManager.sequelize.options.dialect)
  60. smart = Utils.compileSmartWhere.call(self.association.target, smart, self.association.target.daoFactoryManager.sequelize.options.dialect)
  61. if (smart.length > 0) {
  62. options.where = smart
  63. }
  64. }
  65. } else {
  66. options.where = where;
  67. }
  68. self.association.target.findAllJoin(through.tableName, options, queryOptions)
  69. .on('success', function(objects) { customEventEmitter.emit('success', objects) })
  70. .on('error', function(err){ customEventEmitter.emit('error', err) })
  71. .on('sql', function(sql) { customEventEmitter.emit('sql', sql)})
  72. })
  73. return customEventEmitter.run()
  74. }
  75. HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations, defaultAttributes) {
  76. var self = this
  77. , chainer = new Utils.QueryChainer()
  78. , targetAssociation = self.association.targetAssociation
  79. , foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier
  80. , sourceKeys = Object.keys(self.association.source.primaryKeys)
  81. , targetKeys = Object.keys(self.association.target.primaryKeys)
  82. , obsoleteAssociations = []
  83. , changedAssociations = []
  84. , options = {}
  85. , unassociatedObjects;
  86. if ((defaultAttributes || {}).transaction instanceof Transaction) {
  87. options.transaction = defaultAttributes.transaction
  88. delete defaultAttributes.transaction
  89. }
  90. unassociatedObjects = newAssociations.filter(function (obj) {
  91. return !Utils._.find(oldAssociations, function (old) {
  92. return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id)
  93. })
  94. })
  95. oldAssociations.forEach(function (old) {
  96. var newObj = Utils._.find(newAssociations, function (obj) {
  97. return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id)
  98. })
  99. if (!newObj) {
  100. obsoleteAssociations.push(old)
  101. } else if (Object(targetAssociation.through) === targetAssociation.through) {
  102. var throughAttributes = newObj[self.association.through.name];
  103. // Quick-fix for subtle bug when using existing objects that might have the through model attached (not as an attribute object)
  104. if (throughAttributes instanceof self.association.through.DAO) {
  105. throughAttributes = {};
  106. }
  107. var changedAssociation = {
  108. where: {},
  109. attributes: Utils._.defaults({}, throughAttributes, defaultAttributes)
  110. }
  111. changedAssociation.where[self.association.identifier] = self.instance[self.association.identifier] || self.instance[self.association.source.primaryKeyAttributes[0]] || self.instance.id
  112. changedAssociation.where[foreignIdentifier] = newObj[foreignIdentifier] || newObj[self.association.target.primaryKeyAttributes[0]] || newObj.id
  113. if (Object.keys(changedAssociation.attributes).length) {
  114. changedAssociations.push(changedAssociation)
  115. }
  116. }
  117. })
  118. if (obsoleteAssociations.length > 0) {
  119. var foreignIds = obsoleteAssociations.map(function (associatedObject) {
  120. return ((targetKeys.length === 1) ? associatedObject[targetKeys[0]] : associatedObject.id)
  121. })
  122. var where = {}
  123. where[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id)
  124. where[foreignIdentifier] = foreignIds
  125. chainer.add(self.association.through.destroy(where, options))
  126. }
  127. if (unassociatedObjects.length > 0) {
  128. var bulk = unassociatedObjects.map(function(unassociatedObject) {
  129. var attributes = {}
  130. attributes[self.association.identifier] = ((sourceKeys.length === 1) ? self.instance[sourceKeys[0]] : self.instance.id)
  131. attributes[foreignIdentifier] = ((targetKeys.length === 1) ? unassociatedObject[targetKeys[0]] : unassociatedObject.id)
  132. if (Object(targetAssociation.through) === targetAssociation.through) {
  133. attributes = Utils._.defaults(attributes, unassociatedObject[targetAssociation.through.name], defaultAttributes)
  134. }
  135. return attributes
  136. })
  137. chainer.add(self.association.through.bulkCreate(bulk, options))
  138. }
  139. if (changedAssociations.length > 0) {
  140. changedAssociations.forEach(function (assoc) {
  141. chainer.add(self.association.through.update(assoc.attributes, assoc.where, options))
  142. })
  143. }
  144. chainer
  145. .run()
  146. .success(function() { emitterProxy.emit('success', newAssociations) })
  147. .error(function(err) { emitterProxy.emit('error', err) })
  148. .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
  149. }
  150. HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation, additionalAttributes, exists) {
  151. var attributes = {}
  152. , targetAssociation = this.association.targetAssociation
  153. , foreignIdentifier = targetAssociation.isSelfAssociation ? targetAssociation.foreignIdentifier : targetAssociation.identifier
  154. , options = {}
  155. var sourceKeys = Object.keys(this.association.source.primaryKeys);
  156. var targetKeys = Object.keys(this.association.target.primaryKeys);
  157. if ((additionalAttributes || {}).transaction instanceof Transaction) {
  158. options.transaction = additionalAttributes.transaction
  159. delete additionalAttributes.transaction
  160. }
  161. attributes[this.association.identifier] = ((sourceKeys.length === 1) ? this.instance[sourceKeys[0]] : this.instance.id)
  162. attributes[foreignIdentifier] = ((targetKeys.length === 1) ? newAssociation[targetKeys[0]] : newAssociation.id)
  163. if (exists) {
  164. var where = attributes
  165. attributes = Utils._.defaults({}, newAssociation[targetAssociation.through.name], additionalAttributes)
  166. if (Object.keys(attributes).length) {
  167. targetAssociation.through.update(attributes, where).proxy(emitterProxy)
  168. } else {
  169. emitterProxy.emit('success')
  170. }
  171. } else {
  172. attributes = Utils._.defaults(attributes, newAssociation[targetAssociation.through.name], additionalAttributes)
  173. this.association.through.create(attributes, options)
  174. .success(function() { emitterProxy.emit('success', newAssociation) })
  175. .error(function(err) { emitterProxy.emit('error', err) })
  176. .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
  177. }
  178. }
  179. return HasManyDoubleLinked
  180. })()