1 var comb = require("comb"), 2 hitch = comb.hitch, 3 Promise = comb.Promise, 4 PromiseList = comb.PromiseList, 5 OneToMany = require("./oneToMany"); 6 7 var LOGGER = comb.logging.Logger.getLogger("comb.associations.ManyToMany"); 8 /** 9 * @class Class to define a manyToMany association. 10 * 11 * </br> 12 * <b>NOT to be instantiated directly</b> 13 * Its just documented for reference. 14 * 15 * @name ManyToMany 16 * @augments OneToMany 17 * 18 * @param {String} options.joinTable the joinTable of the association. 19 * 20 * 21 * @property {String} joinTable the join table used in the relation. 22 * */ 23 module.exports = exports = comb.define(OneToMany, { 24 instance : { 25 26 _fetchMethod : "all", 27 28 //constructor 29 constructor : function(options) { 30 if (!options.joinTable) { 31 throw new Error("Join table required for a manyToManyRelationship"); 32 } 33 this._joinTable = options.joinTable; 34 this.super(arguments); 35 }, 36 37 38 _filter : function(parent) { 39 if (!this.filter) { 40 if (!this.orderBy) { 41 this.orderBy = this.model.primaryKey; 42 } 43 var q = {}; 44 var jq = {}; 45 jq[this.leftKey] = parent[parent.primaryKey]; 46 q[this.model.table.pk] = {"in" : this.joinTable.dataset.select(this.rightKey).find(jq).sql}; 47 return this.model.filter(q).order(this.orderBy); 48 } else { 49 return this.super(arguments); 50 } 51 }, 52 53 diff : function(next, self) { 54 if (!this[self.loadedKey]) { 55 if (!self.filter || (self.filter && self.isEager())) { 56 this[self.name].then(hitch(this, self.diff, next, self)); 57 } else { 58 next(); 59 } 60 61 } else if (!this.filter) { 62 var values = this[self.addedKey]; 63 var removeValues = this[self.removedKey]; 64 var pl = []; 65 if (values.length) { 66 pl = pl.concat(values.map(function(v) { 67 var p = new Promise(); 68 v.save().then(hitch(this, function(child) { 69 var q = {}; 70 q[self.leftKey] = this[this.primaryKey]; 71 q[self.rightKey] = v[v.primaryKey]; 72 self.joinTable.save(q).then(hitch(p, 'callback'), hitch(p, "errback")); 73 }), hitch(p, "errback")); 74 return p; 75 }, this)); 76 } 77 if (removeValues.length) { 78 var ids = removeValues.map(function(v) { 79 return v.primaryKeyValue; 80 }); 81 var q = {}; 82 q[self.rightKey] = {"in" : ids}; 83 pl.push(self.joinTable.remove(q)); 84 } 85 if (pl.length) { 86 new PromiseList(pl).then(hitch(this, function(r) { 87 this[self.addedKey].length = 0; 88 this[self.removedKey].length = 0; 89 this[self.loadedKey] = true; 90 next(); 91 }), function(err) { 92 err.forEach(function(e) { 93 LOGGER.error(util.inspect(err[0][1][1])); 94 }); 95 next(); 96 }); 97 } else { 98 this[self.loadedKey] = true; 99 next(); 100 } 101 } else { 102 next(); 103 } 104 }, 105 106 _preRemove : function(next, self) { 107 if (this.filter) next(); 108 if (!this[self.loadedKey]) { 109 this[self.name].then(hitch(this, function(values) { 110 this[self.removedKey] = values; 111 self.diff.call(this, next, self); 112 })); 113 } else { 114 this[self.removedKey] = this[self.name]; 115 self.diff.call(this, next, self); 116 } 117 118 }, 119 120 getters : { 121 122 //returns our join table model 123 joinTable : function() { 124 return this.moose.getModel(this._joinTable); 125 } 126 } 127 } 128 }); 129 130