1 var associations = require("../associations"), 2 oneToMany = associations.oneToMany, 3 manyToOne = associations.manyToOne, 4 oneToOne = associations.oneToOne, 5 manyToMany = associations.manyToMany, 6 fetch = associations.fetch, 7 comb = require("comb"); 8 9 /** 10 * @class 11 * <p>plugin to expose association capability.</p> 12 * 13 * The associations exposed include 14 * 15 * 16 * 17 * 18 * <ul> 19 * <li>oneToMany - Foreign key in associated model's table points to this 20 * model's primary key. Each current model object can be associated with 21 * more than one associated model objects. Each associated model object 22 * can be associated with only one current model object.</li> 23 * <li>manyToOne - Foreign key in current model's table points to 24 * associated model's primary key. Each associated model object can 25 * be associated with more than one current model objects. Each current 26 * model object can be associated with only one associated model object.</li> 27 * <li>oneToOne - Similar to one_to_many in terms of foreign keys, but 28 * only one object is associated to the current object through the 29 * association. The methods created are similar to many_to_one, except 30 * that the one_to_one setter method saves the passed object./li> 31 * <li>manyToMany - A join table is used that has a foreign key that points 32 * to this model's primary key and a foreign key that points to the 33 * associated model's primary key. Each current model object can be 34 * associated with many associated model objects, and each associated 35 * model object can be associated with many current model objects./li> 36 * </ul> 37 * 38 * @name AssociationPlugin 39 * @memberOf moose.plugins 40 * 41 */ 42 exports.AssociationPlugin = comb.define(null, { 43 static : { 44 /**@lends moose.plugins.AssociationPlugin*/ 45 46 /** 47 * One of the most common forms of associtaions. One to Many is the inverse of Many to one. One to Many often describes a parent child reationship, 48 * where the One To many Model is the parent, and the many to one model is the child. 49 * <p>For example consider a BiologicalFather and his children. The father can have many children, but a child can have only one Biological Father.</p> 50 * 51 * @example 52 * biological_father child 53 * -------------_ ------------------------- 54 * |id | name | |id | bioFatherId | name | 55 * -------------- ------------------------- 56 * | 1 | Fred | | 1 | 1 | Bobby | 57 * | 2 | Ben | ------> | 2 | 1 | Alice | 58 * | 3 | Bob | | 3 | 1 | Susan | 59 * | 4 | Scott | | 4 | 4 | Brad | 60 * -------------- ------------------------- 61 * 62 * @example 63 * 64 * //define Social security model 65 * var BioFather = moose.addModel("biological_father"); 66 * 67 * //define Person model 68 * var Child = moose.addModel("child"); 69 * 70 * //Create oneToMany relationship from father to child 71 * BioFather.oneToMany("children", { 72 * model : Child.tableName, 73 * key : {id : "bioFatherId"} 74 * }); 75 * 76 * 77 * //Create oneToOne relation ship from ssn to person with a fetchtype of eager. 78 * Child.manyToOne("biologicalFather", { 79 * model : BioFather.tableName, 80 * fetchType : BioFather.fetchType.EAGER, 81 * key : {bioFatherId : "id"} 82 * }); 83 * 84 * Child.findById(1).then(function(child){ 85 * child.father.name => "fred" 86 * }); 87 * 88 * BioFather.findById(1).then(function(father){ 89 * father.children.then(function(children){ 90 * children.length => 3 91 * }); 92 * }); 93 * 94 * </p> 95 * @param {String} name the alias of the association. The key you provide here is how the association 96 * will be looked up on instances of this model. 97 * @param {Object} options object that describes the association. 98 * @param {String} options.model the table name of the model that this Model is associated with. 99 * @param {Function} options.filter Custom filter to define a custom association. 100 * The filter is called in the scope of model that the association is added to. 101 * Say we have a model called BioFather that is a one to many to a model called Child. 102 * <pre class="code"> 103 * BioFather.oneToMany("children", { 104 * model : Child.tableName, 105 * fetchType : BioFather.fetchType.EAGER, 106 * filter : function(){ 107 * return Child.filter({bioFatherId : this.id}); 108 * } 109 * }); 110 * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY] 111 * how fetch the association, if specified to lazy then the association is lazy loaded. 112 * Otherwise the association is loaded when the model is loaded. 113 * @param {Object} key this defines the foreign key relationship 114 * <pre class="code"> 115 * {thisModelsKey : otherModelsKey} 116 * </pre> 117 * @param {String|Object} [options.orderBy] column or columns to order the associated model by. 118 */ 119 oneToMany : function(name, options) { 120 var assoc = new oneToMany(options, this.moose); 121 assoc.inject(this, name); 122 }, 123 124 /** 125 * See {@link AssociationPlugin.oneToMany} 126 * @param {String} name the alias of the association. The key you provide here is how the association 127 * will be looked up on instances of this model. 128 * @param {Object} options object that describes the association. 129 * @param {String} options.model the table name of the model that this Model is associated with. 130 * @param {Function} options.filter Custom filter to define a custom association. 131 * The filter is called in the scope of model that the association is added to. 132 * Say we have a model called Child that is a many to one to a model called BioFather. 133 * <pre class="code"> 134 * Child.manyToOne("biologicalFather", { 135 * model : BioFather.tableName, 136 * fetchType : BioFather.fetchType.EAGER, 137 * filter : function(){ 138 * return BioFather.filter({id : this.bidFatherId}); 139 * } 140 * }); 141 * 142 * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY] 143 * how fetch the association, if specified to lazy then the association is lazy loaded. 144 * Otherwise the association is loaded when the model is loaded. 145 * @param {Object} key this defines the foreign key relationship 146 * @param {String|Array} [options.orderBy] column or columns to order the associated model by. 147 * <pre class="code"> 148 * {thisModelsKey : otherModelsKey} 149 * </pre> 150 * @param {String|Object} [options.orderBy] column or columns to order the associated model by. 151 */ 152 manyToOne : function(name, options) { 153 var assoc = new manyToOne(options, this.moose); 154 assoc.inject(this, name); 155 }, 156 157 /** 158 * <p>Simplest form of association. This describes where there is a one to one relationship between classes.</p> 159 * 160 * When createing a reciprocal one to one relationship between models one of the models should be a many to one association. 161 * The table that contains the foreign key should contain have the manyToOne relationship. 162 * 163 * <p>For example consider social security numbers. There is one social security per person, this would be considered a one to one relationship. 164 * 165 * @example 166 * Person SSN NUMBER 167 *------------------------- --------------- 168 *|id | ssn | |id | 169 *------------------------- --------------- 170 *|00000001 | 111111111 | | 111111111 | 171 *| ......... | ......... | ------> | ......... | 172 *| ......... | ......... | | ......... | 173 *| nnnnnnnnn | nnnnnnnn | | nnnnnnnnn | 174 *------------------------- --------------- 175 * 176 * @example 177 * 178 * //define Social security model 179 * var SocialSecurityNumber = moose.addModel("ssn"); 180 * 181 * //define Person model 182 * var Person = moose.addModel("person"); 183 * 184 * //Create oneToOne relation ship from ssn to person with a fetchtype of eager. 185 * SocialSecurityNumber.oneToOne("person", { 186 * model : Person.tableName, 187 * fetchType : SocialSecurityNumber.fetchType.EAGER, 188 * key : {id : "ssn"} 189 * }); 190 * 191 * //Create oneToMany relation ship from person to ssn, 192 * //It is many to one because is contains the ssn foreign key. 193 * 194 * Person.manyToOne("ssn", { 195 * model : SocialSecurityNumber.tableName, 196 * key : {ssn : "id"} 197 * }); 198 * 199 * 200 201 * 202 * Person.findById(1).then(function(person){ 203 * person.ssn.then(function(ssn){ 204 * ssn.id => 111111111 205 * }); 206 * }); 207 * 208 * SocialSecurityNumber.findById(111111111).then(function(ssn){ 209 * ssn.person.id => 1 210 * }); 211 * </p> 212 * @param {String} name the alias of the association. The key you provide here is how the association 213 * will be looked up on instances of this model. 214 * @param {Object} options object that describes the association. 215 * @param {String} options.model the table name of the model that this Model is associated with. 216 * @param {Function} options.filter Custom filter to define a custom association. 217 * The filter is called in the scope of model that the association is added to. 218 * Say we have the same models as defined above. 219 * <pre class="code"> 220 * SocialSecurityNumber.oneToOne("person", { 221 * model : Person.tableName, 222 * filter : function(){ 223 * //find the worker that has my id. 224 * return Person.filter({ssn : this.id}); 225 * } 226 * }); 227 * </pre> 228 * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY] 229 * how fetch the association, if specified to lazy then the association is lazy loaded. 230 * Otherwise the association is loaded when the model is loaded. 231 * @param {Object} key this defines the foreign key relationship 232 * <pre class="code"> 233 * {thisModelsKey : otherModelsKey} 234 * </pre> 235 * @param {String|Object} [options.orderBy] column or columns to order the associated model by. 236 */ 237 oneToOne : function(name, options) { 238 var assoc = new oneToOne(options, this.moose); 239 assoc.inject(this, name); 240 }, 241 242 /** 243 * The manyToMany association allows a model to be associated to many other rows in another model. 244 * and the associated model can be associated with many rows in this model. This is done by 245 * using a join table to associate the two models. 246 * <p>For example consider phone numbers. Each person can have multiple phone numbers. 247 * 248 * @example 249 * phone person_phone person 250 * ------ ---------------------- ----- 251 * |id | |person_id | phone_id| |id | 252 * ------ ---------------------- ----- 253 * | 1 | | 1 | 1 | | 1 | 254 * | . | <------ | 1 | 2 | ------> | 2 | 255 * | . | | 2 | 2 | | 3 | 256 * | n | | 2 | 1 | | 4 | 257 * ------ ---------------------- ----- 258 * 259 * @example 260 * 261 * //define the PhoneNumber model 262 * var PhoneNumber = moose.addModel("phone"); 263 * 264 * //define Person model 265 * var Person = moose.addModel("person"); 266 * 267 * //Create manyToMany relationship from person to PhoneNumber 268 * Person.manyToMany("phoneNumbers", { 269 * model : PhoneNumber.tableName, 270 * joinTable : "person_phone", 271 * key : {person_id : "phone_id"} 272 *}); 273 * 274 * 275 * PhoneNumber.manyToMany("owners", { 276 * model : Person.tableName, 277 * joinTable : "person_phone", 278 * key : {phone_id : "person_id"} 279 *}); 280 * 281 * Person.findById(1).then(function(person){ 282 * person.phoneNumbers.then(function(numbers){ 283 * numbers.length => 2 284 * }); 285 * }); 286 * 287 * PhoneNumber.findById(1).then(function(number){ 288 * number.owners.then(function(owners){ 289 * owners.length => 2; 290 * }); 291 * }); 292 * </p> 293 * @param {String} name the alias of the association. The key you provide here is how the association 294 * will be looked up on instances of this model. 295 * @param {Object} options object that describes the association. 296 * @param {String} options.model the table name of the model that this Model is associated with. 297 * @param {String} options.joinTable the name of the joining table. 298 * @param {Function} options.filter Custom filter to define a custom association. 299 * The filter is called in the scope of model that the association is added to. 300 * Say we have the same models as defined above. 301 * <pre class="code"> 302 * //Define the join table model so we can query it. 303 * PersonPhone = moose.addModel(person_phone); 304 * PhoneNumber.manyToMany("owners", { 305 * model : Person.tableName, 306 * joinTable : "person_phone", 307 * filter : function(){ 308 * //find all the person ids 309 * var jd = PhoneNumber.dataset 310 * .select('person_id') 311 * .find({phone_id : this.id}); 312 * //now query person with the ids! 313 * return Person.filter({id : {"in" : jd}}); 314 * } 315 * }); 316 * </pre> 317 * @param {AssociationPlugin.fetchType.EAGER|AssociationPlugin.fetchType.EAGER} [options.fetchType=AssociationPlugin.fetchType.LAZY] 318 * how fetch the association, if specified to lazy then the association is lazy loaded. 319 * Otherwise the association is loaded when the model is loaded. 320 * @param {Object} key this defines the foreign key relationship 321 * <pre class="code"> 322 * {thisModelsKey : otherModelsKey} 323 * </pre> 324 * @param {String|Object} [options.orderBy] column or columns to order the associated model by. 325 */ 326 manyToMany : function(name, options) { 327 var assoc = new manyToMany(options, this.moose); 328 assoc.inject(this, name); 329 }, 330 331 /** 332 * @borrows _Association.fetch as fetch 333 */ 334 fetchType : fetch 335 }});