Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / data / Association.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @author Ed Spencer
17  *
18  * Associations enable you to express relationships between different {@link Ext.data.Model Models}. Let's say we're
19  * writing an ecommerce system where Users can make Orders - there's a relationship between these Models that we can
20  * express like this:
21  *
22  *     Ext.define('User', {
23  *         extend: 'Ext.data.Model',
24  *         fields: ['id', 'name', 'email'],
25  *
26  *         hasMany: {model: 'Order', name: 'orders'}
27  *     });
28  *
29  *     Ext.define('Order', {
30  *         extend: 'Ext.data.Model',
31  *         fields: ['id', 'user_id', 'status', 'price'],
32  *
33  *         belongsTo: 'User'
34  *     });
35  *
36  * We've set up two models - User and Order - and told them about each other. You can set up as many associations on
37  * each Model as you need using the two default types - {@link Ext.data.HasManyAssociation hasMany} and {@link
38  * Ext.data.BelongsToAssociation belongsTo}. There's much more detail on the usage of each of those inside their
39  * documentation pages. If you're not familiar with Models already, {@link Ext.data.Model there is plenty on those too}.
40  *
41  * **Further Reading**
42  *
43  *   - {@link Ext.data.HasManyAssociation hasMany associations}
44  *   - {@link Ext.data.BelongsToAssociation belongsTo associations}
45  *   - {@link Ext.data.Model using Models}
46  *
47  * # Self association models
48  *
49  * We can also have models that create parent/child associations between the same type. Below is an example, where
50  * groups can be nested inside other groups:
51  *
52  *     // Server Data
53  *     {
54  *         "groups": {
55  *             "id": 10,
56  *             "parent_id": 100,
57  *             "name": "Main Group",
58  *             "parent_group": {
59  *                 "id": 100,
60  *                 "parent_id": null,
61  *                 "name": "Parent Group"
62  *             },
63  *             "child_groups": [{
64  *                 "id": 2,
65  *                 "parent_id": 10,
66  *                 "name": "Child Group 1"
67  *             },{
68  *                 "id": 3,
69  *                 "parent_id": 10,
70  *                 "name": "Child Group 2"
71  *             },{
72  *                 "id": 4,
73  *                 "parent_id": 10,
74  *                 "name": "Child Group 3"
75  *             }]
76  *         }
77  *     }
78  *
79  *     // Client code
80  *     Ext.define('Group', {
81  *         extend: 'Ext.data.Model',
82  *         fields: ['id', 'parent_id', 'name'],
83  *         proxy: {
84  *             type: 'ajax',
85  *             url: 'data.json',
86  *             reader: {
87  *                 type: 'json',
88  *                 root: 'groups'
89  *             }
90  *         },
91  *         associations: [{
92  *             type: 'hasMany',
93  *             model: 'Group',
94  *             primaryKey: 'id',
95  *             foreignKey: 'parent_id',
96  *             autoLoad: true,
97  *             associationKey: 'child_groups' // read child data from child_groups
98  *         }, {
99  *             type: 'belongsTo',
100  *             model: 'Group',
101  *             primaryKey: 'id',
102  *             foreignKey: 'parent_id',
103  *             associationKey: 'parent_group' // read parent data from parent_group
104  *         }]
105  *     });
106  *
107  *     Ext.onReady(function(){
108  *
109  *         Group.load(10, {
110  *             success: function(group){
111  *                 console.log(group.getGroup().get('name'));
112  *
113  *                 group.groups().each(function(rec){
114  *                     console.log(rec.get('name'));
115  *                 });
116  *             }
117  *         });
118  *
119  *     });
120  *
121  */
122 Ext.define('Ext.data.Association', {
123     /**
124      * @cfg {String} ownerModel (required)
125      * The string name of the model that owns the association.
126      */
127
128     /**
129      * @cfg {String} associatedModel (required)
130      * The string name of the model that is being associated with.
131      */
132
133     /**
134      * @cfg {String} primaryKey
135      * The name of the primary key on the associated model. In general this will be the
136      * {@link Ext.data.Model#idProperty} of the Model.
137      */
138     primaryKey: 'id',
139
140     /**
141      * @cfg {Ext.data.reader.Reader} reader
142      * A special reader to read associated data
143      */
144     
145     /**
146      * @cfg {String} associationKey
147      * The name of the property in the data to read the association from. Defaults to the name of the associated model.
148      */
149
150     defaultReaderType: 'json',
151
152     statics: {
153         create: function(association){
154             if (!association.isAssociation) {
155                 if (Ext.isString(association)) {
156                     association = {
157                         type: association
158                     };
159                 }
160
161                 switch (association.type) {
162                     case 'belongsTo':
163                         return Ext.create('Ext.data.BelongsToAssociation', association);
164                     case 'hasMany':
165                         return Ext.create('Ext.data.HasManyAssociation', association);
166                     //TODO Add this back when it's fixed
167 //                    case 'polymorphic':
168 //                        return Ext.create('Ext.data.PolymorphicAssociation', association);
169                     default:
170                         //<debug>
171                         Ext.Error.raise('Unknown Association type: "' + association.type + '"');
172                         //</debug>
173                 }
174             }
175             return association;
176         }
177     },
178
179     /**
180      * Creates the Association object.
181      * @param {Object} [config] Config object.
182      */
183     constructor: function(config) {
184         Ext.apply(this, config);
185
186         var types           = Ext.ModelManager.types,
187             ownerName       = config.ownerModel,
188             associatedName  = config.associatedModel,
189             ownerModel      = types[ownerName],
190             associatedModel = types[associatedName],
191             ownerProto;
192
193         //<debug>
194         if (ownerModel === undefined) {
195             Ext.Error.raise("The configured ownerModel was not valid (you tried " + ownerName + ")");
196         }
197         if (associatedModel === undefined) {
198             Ext.Error.raise("The configured associatedModel was not valid (you tried " + associatedName + ")");
199         }
200         //</debug>
201
202         this.ownerModel = ownerModel;
203         this.associatedModel = associatedModel;
204
205         /**
206          * @property {String} ownerName
207          * The name of the model that 'owns' the association
208          */
209
210         /**
211          * @property {String} associatedName
212          * The name of the model is on the other end of the association (e.g. if a User model hasMany Orders, this is
213          * 'Order')
214          */
215
216         Ext.applyIf(this, {
217             ownerName : ownerName,
218             associatedName: associatedName
219         });
220     },
221
222     /**
223      * Get a specialized reader for reading associated data
224      * @return {Ext.data.reader.Reader} The reader, null if not supplied
225      */
226     getReader: function(){
227         var me = this,
228             reader = me.reader,
229             model = me.associatedModel;
230
231         if (reader) {
232             if (Ext.isString(reader)) {
233                 reader = {
234                     type: reader
235                 };
236             }
237             if (reader.isReader) {
238                 reader.setModel(model);
239             } else {
240                 Ext.applyIf(reader, {
241                     model: model,
242                     type : me.defaultReaderType
243                 });
244             }
245             me.reader = Ext.createByAlias('reader.' + reader.type, reader);
246         }
247         return me.reader || null;
248     }
249 });
250