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