has-many.js
4.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
var Utils = require("./../utils")
, DataTypes = require('./../data-types')
var HasManySingleLinked = require("./has-many-single-linked")
, HasManyMultiLinked = require("./has-many-double-linked")
var HasMany = module.exports = function(srcModel, targetModel, options) {
this.source = srcModel
this.target = targetModel
this.options = options
this.isSelfAssociation = (this.source.tableName == this.target.tableName)
this.combinedName = Utils.combineTableNames(
this.source.tableName,
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
)
var as = (this.options.as || Utils.pluralize(this.target.tableName))
this.accessors = {
get: Utils._.camelize('get_' + as),
set: Utils._.camelize('set_' + as),
add: Utils._.camelize(Utils.singularize('add_' + as)),
remove: Utils._.camelize(Utils.singularize('remove_' + as))
}
}
// the id is in the target table
// or in an extra table which connects two tables
HasMany.prototype.injectAttributes = function() {
var multiAssociation = this.target.associations.hasOwnProperty(this.combinedName)
this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName) + "Id", this.options.underscored)
// is there already a single sided association between the source and the target?
// or is the association on the model itself?
if (this.isSelfAssociation || multiAssociation) {
// remove the obsolete association identifier from the source
if(this.isSelfAssociation) {
this.foreignIdentifier = (this.options.as || this.target.tableName) + 'Id'
} else {
this.foreignIdentifier = this.target.associations[this.combinedName].identifier
delete this.source.attributes[this.foreignIdentifier]
}
// define a new model, which connects the models
var combinedTableAttributes = {}
combinedTableAttributes[this.identifier] = {type:DataTypes.INTEGER, primaryKey: true}
combinedTableAttributes[this.foreignIdentifier] = {type:DataTypes.INTEGER, primaryKey: true}
this.connectorModel = this.source.modelManager.sequelize.define( this.combinedName, combinedTableAttributes )
if(!this.isSelfAssociation) this.target.associations[this.combinedName].connectorModel = this.connectorModel
this.connectorModel.sync()
} else {
var newAttributes = {}
newAttributes[this.identifier] = { type: DataTypes.INTEGER }
Utils._.extend(this.target.attributes, Utils.simplifyAttributes(newAttributes))
}
return this
}
HasMany.prototype.injectGetter = function(obj) {
var self = this
obj[this.accessors.get] = function() {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
return new Class(self, this).injectGetter()
}
return this
}
HasMany.prototype.injectSetter = function(obj) {
var self = this
obj[this.accessors.set] = function(newAssociatedObjects) {
var instance = this
// define the returned customEventEmitter, which will emit the success event once everything is done
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().on('success', function(oldAssociatedObjects) {
var Class = self.connectorModel ? HasManyMultiLinked : HasManySingleLinked
new Class(self, instance).injectSetter(customEventEmitter, oldAssociatedObjects, newAssociatedObjects)
})
})
return customEventEmitter.run()
}
obj[this.accessors.add] = function(newAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.on('failure', function(err){ customEventEmitter.emit('failure', err)})
.on('success', function(currentAssociatedObjects) {
if(!newAssociatedObject.equalsOneOf(currentAssociatedObjects))
currentAssociatedObjects.push(newAssociatedObject)
instance[self.accessors.set](currentAssociatedObjects)
.on('success', function(instances) { customEventEmitter.emit('success', instances) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
})
})
return customEventEmitter.run()
}
obj[this.accessors.remove] = function(oldAssociatedObject) {
var instance = this
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]().on('success', function(currentAssociatedObjects) {
var newAssociations = []
currentAssociatedObjects.forEach(function(association) {
if(!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers))
newAssociations.push(association)
})
instance[self.accessors.set](newAssociations)
.on('success', function() { customEventEmitter.emit('success', null) })
.on('failure', function(err) { customEventEmitter.emit('failure', err) })
})
})
return customEventEmitter.run()
}
return this
}