      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     }});