      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