Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / HasManyAssociation.html
1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-data.HasManyAssociation'>/**
2 </span> * @author Ed Spencer
3  * @class Ext.data.HasManyAssociation
4  * @extends Ext.data.Association
5  * 
6  * &lt;p&gt;Represents a one-to-many relationship between two models. Usually created indirectly via a model definition:&lt;/p&gt;
7  * 
8 &lt;pre&gt;&lt;code&gt;
9 Ext.define('Product', {
10     extend: 'Ext.data.Model',
11     fields: [
12         {name: 'id',      type: 'int'},
13         {name: 'user_id', type: 'int'},
14         {name: 'name',    type: 'string'}
15     ]
16 });
17
18 Ext.define('User', {
19     extend: 'Ext.data.Model',
20     fields: [
21         {name: 'id',   type: 'int'},
22         {name: 'name', type: 'string'}
23     ],
24     // we can use the hasMany shortcut on the model to create a hasMany association
25     hasMany: {model: 'Product', name: 'products'}
26 });
27 &lt;/pre&gt;&lt;/code&gt;
28
29  * &lt;p&gt;Above we created Product and User models, and linked them by saying that a User hasMany Products. This gives
30  * us a new function on every User instance, in this case the function is called 'products' because that is the name
31  * we specified in the association configuration above.&lt;/p&gt;
32  * 
33  * &lt;p&gt;This new function returns a specialized {@link Ext.data.Store Store} which is automatically filtered to load
34  * only Products for the given model instance:&lt;/p&gt;
35  * 
36 &lt;pre&gt;&lt;code&gt;
37 //first, we load up a User with id of 1
38 var user = Ext.ModelManager.create({id: 1, name: 'Ed'}, 'User');
39
40 //the user.products function was created automatically by the association and returns a {@link Ext.data.Store Store}
41 //the created store is automatically scoped to the set of Products for the User with id of 1
42 var products = user.products();
43
44 //we still have all of the usual Store functions, for example it's easy to add a Product for this User
45 products.add({
46     name: 'Another Product'
47 });
48
49 //saves the changes to the store - this automatically sets the new Product's user_id to 1 before saving
50 products.sync();
51 &lt;/code&gt;&lt;/pre&gt;
52  * 
53  * &lt;p&gt;The new Store is only instantiated the first time you call products() to conserve memory and processing time,
54  * though calling products() a second time returns the same store instance.&lt;/p&gt;
55  * 
56  * &lt;p&gt;&lt;u&gt;Custom filtering&lt;/u&gt;&lt;/p&gt;
57  * 
58  * &lt;p&gt;The Store is automatically furnished with a filter - by default this filter tells the store to only return
59  * records where the associated model's foreign key matches the owner model's primary key. For example, if a User
60  * with ID = 100 hasMany Products, the filter loads only Products with user_id == 100.&lt;/p&gt;
61  * 
62  * &lt;p&gt;Sometimes we want to filter by another field - for example in the case of a Twitter search application we may
63  * have models for Search and Tweet:&lt;/p&gt;
64  * 
65 &lt;pre&gt;&lt;code&gt;
66 Ext.define('Search', {
67     extend: 'Ext.data.Model',
68     fields: [
69         'id', 'query'
70     ],
71
72     hasMany: {
73         model: 'Tweet',
74         name : 'tweets',
75         filterProperty: 'query'
76     }
77 });
78
79 Ext.define('Tweet', {
80     extend: 'Ext.data.Model',
81     fields: [
82         'id', 'text', 'from_user'
83     ]
84 });
85
86 //returns a Store filtered by the filterProperty
87 var store = new Search({query: 'Sencha Touch'}).tweets();
88 &lt;/code&gt;&lt;/pre&gt;
89  * 
90  * &lt;p&gt;The tweets association above is filtered by the query property by setting the {@link #filterProperty}, and is
91  * equivalent to this:&lt;/p&gt;
92  * 
93 &lt;pre&gt;&lt;code&gt;
94 var store = new Ext.data.Store({
95     model: 'Tweet',
96     filters: [
97         {
98             property: 'query',
99             value   : 'Sencha Touch'
100         }
101     ]
102 });
103 &lt;/code&gt;&lt;/pre&gt;
104  */
105 Ext.define('Ext.data.HasManyAssociation', {
106     extend: 'Ext.data.Association',
107     requires: ['Ext.util.Inflector'],
108
109     alias: 'association.hasmany',
110
111 <span id='Ext-data.HasManyAssociation-cfg-foreignKey'>    /**
112 </span>     * @cfg {String} foreignKey The name of the foreign key on the associated model that links it to the owner
113      * model. Defaults to the lowercased name of the owner model plus &quot;_id&quot;, e.g. an association with a where a
114      * model called Group hasMany Users would create 'group_id' as the foreign key. When the remote store is loaded,
115      * the store is automatically filtered so that only records with a matching foreign key are included in the 
116      * resulting child store. This can be overridden by specifying the {@link #filterProperty}.
117      * &lt;pre&gt;&lt;code&gt;
118 Ext.define('Group', {
119     extend: 'Ext.data.Model',
120     fields: ['id', 'name'],
121     hasMany: 'User'
122 });
123
124 Ext.define('User', {
125     extend: 'Ext.data.Model',
126     fields: ['id', 'name', 'group_id'], // refers to the id of the group that this user belongs to
127     belongsTo: 'Group'
128 });
129      * &lt;/code&gt;&lt;/pre&gt;
130      */
131     
132 <span id='Ext-data.HasManyAssociation-cfg-name'>    /**
133 </span>     * @cfg {String} name The name of the function to create on the owner model to retrieve the child store.
134      * If not specified, the pluralized name of the child model is used.
135      * &lt;pre&gt;&lt;code&gt;
136 // This will create a users() method on any Group model instance
137 Ext.define('Group', {
138     extend: 'Ext.data.Model',
139     fields: ['id', 'name'],
140     hasMany: 'User'
141 });
142 var group = new Group();
143 console.log(group.users());
144
145 // The method to retrieve the users will now be getUserList
146 Ext.define('Group', {
147     extend: 'Ext.data.Model',
148     fields: ['id', 'name'],
149     hasMany: {model: 'User', name: 'getUserList'}
150 });
151 var group = new Group();
152 console.log(group.getUserList());
153      * &lt;/code&gt;&lt;/pre&gt;
154      */
155     
156 <span id='Ext-data.HasManyAssociation-cfg-storeConfig'>    /**
157 </span>     * @cfg {Object} storeConfig Optional configuration object that will be passed to the generated Store. Defaults to 
158      * undefined.
159      */
160     
161 <span id='Ext-data.HasManyAssociation-cfg-filterProperty'>    /**
162 </span>     * @cfg {String} filterProperty Optionally overrides the default filter that is set up on the associated Store. If
163      * this is not set, a filter is automatically created which filters the association based on the configured 
164      * {@link #foreignKey}. See intro docs for more details. Defaults to undefined
165      */
166     
167 <span id='Ext-data.HasManyAssociation-cfg-autoLoad'>    /**
168 </span>     * @cfg {Boolean} autoLoad True to automatically load the related store from a remote source when instantiated.
169      * Defaults to &lt;tt&gt;false&lt;/tt&gt;.
170      */
171     
172 <span id='Ext-data.HasManyAssociation-cfg-type'>    /**
173 </span>     * @cfg {String} type The type configuration can be used when creating associations using a configuration object.
174      * Use 'hasMany' to create a HasManyAssocation
175      * &lt;pre&gt;&lt;code&gt;
176 associations: [{
177     type: 'hasMany',
178     model: 'User'
179 }]
180      * &lt;/code&gt;&lt;/pre&gt;
181      */
182     
183     constructor: function(config) {
184         var me = this,
185             ownerProto,
186             name;
187             
188         me.callParent(arguments);
189         
190         me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase());
191         
192         ownerProto = me.ownerModel.prototype;
193         name = me.name;
194         
195         Ext.applyIf(me, {
196             storeName : name + &quot;Store&quot;,
197             foreignKey: me.ownerName.toLowerCase() + &quot;_id&quot;
198         });
199         
200         ownerProto[name] = me.createStore();
201     },
202     
203 <span id='Ext-data.HasManyAssociation-method-createStore'>    /**
204 </span>     * @private
205      * Creates a function that returns an Ext.data.Store which is configured to load a set of data filtered
206      * by the owner model's primary key - e.g. in a hasMany association where Group hasMany Users, this function
207      * returns a Store configured to return the filtered set of a single Group's Users.
208      * @return {Function} The store-generating function
209      */
210     createStore: function() {
211         var that            = this,
212             associatedModel = that.associatedModel,
213             storeName       = that.storeName,
214             foreignKey      = that.foreignKey,
215             primaryKey      = that.primaryKey,
216             filterProperty  = that.filterProperty,
217             autoLoad        = that.autoLoad,
218             storeConfig     = that.storeConfig || {};
219         
220         return function() {
221             var me = this,
222                 config, filter,
223                 modelDefaults = {};
224                 
225             if (me[storeName] === undefined) {
226                 if (filterProperty) {
227                     filter = {
228                         property  : filterProperty,
229                         value     : me.get(filterProperty),
230                         exactMatch: true
231                     };
232                 } else {
233                     filter = {
234                         property  : foreignKey,
235                         value     : me.get(primaryKey),
236                         exactMatch: true
237                     };
238                 }
239                 
240                 modelDefaults[foreignKey] = me.get(primaryKey);
241                 
242                 config = Ext.apply({}, storeConfig, {
243                     model        : associatedModel,
244                     filters      : [filter],
245                     remoteFilter : false,
246                     modelDefaults: modelDefaults
247                 });
248                 
249                 me[storeName] = Ext.create('Ext.data.Store', config);
250                 if (autoLoad) {
251                     me[storeName].load();
252                 }
253             }
254             
255             return me[storeName];
256         };
257     },
258     
259 <span id='Ext-data.HasManyAssociation-method-read'>    /**
260 </span>     * Read associated data
261      * @private
262      * @param {Ext.data.Model} record The record we're writing to
263      * @param {Ext.data.reader.Reader} reader The reader for the associated model
264      * @param {Object} associationData The raw associated data
265      */
266     read: function(record, reader, associationData){
267         var store = record[this.name](),
268             inverse;
269     
270         store.add(reader.read(associationData).records);
271     
272         //now that we've added the related records to the hasMany association, set the inverse belongsTo
273         //association on each of them if it exists
274         inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
275             return assoc.type === 'belongsTo' &amp;&amp; assoc.associatedName === record.$className;
276         });
277     
278         //if the inverse association was found, set it now on each record we've just created
279         if (inverse) {
280             store.data.each(function(associatedRecord){
281                 associatedRecord[inverse.instanceName] = record;
282             });
283         }
284     }
285 });</pre></pre></body></html>