BelongsTo.html
12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js"><span id='Ext-data-association-BelongsTo'>/**
</span> * @author Ed Spencer
* @class Ext.data.association.BelongsTo
*
* Represents a many to one association with another model. The owner model is expected to have
* a foreign key which references the primary key of the associated model:
*
* Ext.define('Category', {
* extend: 'Ext.data.Model',
* fields: [
* { name: 'id', type: 'int' },
* { name: 'name', type: 'string' }
* ]
* });
*
* Ext.define('Product', {
* extend: 'Ext.data.Model',
* fields: [
* { name: 'id', type: 'int' },
* { name: 'category_id', type: 'int' },
* { name: 'name', type: 'string' }
* ],
* // we can use the belongsTo shortcut on the model to create a belongsTo association
* associations: [
* { type: 'belongsTo', model: 'Category' }
* ]
* });
*
* In the example above we have created models for Products and Categories, and linked them together
* by saying that each Product belongs to a Category. This automatically links each Product to a Category
* based on the Product's category_id, and provides new functions on the Product model:
*
* ## Generated getter function
*
* The first function that is added to the owner model is a getter function:
*
* var product = new Product({
* id: 100,
* category_id: 20,
* name: 'Sneakers'
* });
*
* product.getCategory(function(category, operation) {
* // do something with the category object
* alert(category.get('id')); // alerts 20
* }, this);
*
* The getCategory function was created on the Product model when we defined the association. This uses the
* Category's configured {@link Ext.data.proxy.Proxy proxy} to load the Category asynchronously, calling the provided
* callback when it has loaded.
*
* The new getCategory function will also accept an object containing success, failure and callback properties
* - callback will always be called, success will only be called if the associated model was loaded successfully
* and failure will only be called if the associatied model could not be loaded:
*
* product.getCategory({
* reload: true, // force a reload if the owner model is already cached
* callback: function(category, operation) {}, // a function that will always be called
* success : function(category, operation) {}, // a function that will only be called if the load succeeded
* failure : function(category, operation) {}, // a function that will only be called if the load did not succeed
* scope : this // optionally pass in a scope object to execute the callbacks in
* });
*
* In each case above the callbacks are called with two arguments - the associated model instance and the
* {@link Ext.data.Operation operation} object that was executed to load that instance. The Operation object is
* useful when the instance could not be loaded.
*
* Once the getter has been called on the model, it will be cached if the getter is called a second time. To
* force the model to reload, specify reload: true in the options object.
*
* ## Generated setter function
*
* The second generated function sets the associated model instance - if only a single argument is passed to
* the setter then the following two calls are identical:
*
* // this call...
* product.setCategory(10);
*
* // is equivalent to this call:
* product.set('category_id', 10);
*
* An instance of the owner model can also be passed as a parameter.
*
* If we pass in a second argument, the model will be automatically saved and the second argument passed to
* the owner model's {@link Ext.data.Model#save save} method:
*
* product.setCategory(10, function(product, operation) {
* // the product has been saved
* alert(product.get('category_id')); //now alerts 10
* });
*
* //alternative syntax:
* product.setCategory(10, {
* callback: function(product, operation), // a function that will always be called
* success : function(product, operation), // a function that will only be called if the load succeeded
* failure : function(product, operation), // a function that will only be called if the load did not succeed
* scope : this //optionally pass in a scope object to execute the callbacks in
* })
*
* ## Customisation
*
* Associations reflect on the models they are linking to automatically set up properties such as the
* {@link #primaryKey} and {@link #foreignKey}. These can alternatively be specified:
*
* Ext.define('Product', {
* fields: [...],
*
* associations: [
* { type: 'belongsTo', model: 'Category', primaryKey: 'unique_id', foreignKey: 'cat_id' }
* ]
* });
*
* Here we replaced the default primary key (defaults to 'id') and foreign key (calculated as 'category_id')
* with our own settings. Usually this will not be needed.
*/
Ext.define('Ext.data.association.BelongsTo', {
extend: 'Ext.data.association.Association',
alternateClassName: 'Ext.data.BelongsToAssociation',
alias: 'association.belongsto',
<span id='Ext-data-association-BelongsTo-cfg-foreignKey'> /**
</span> * @cfg {String} foreignKey The name of the foreign key on the owner model that links it to the associated
* model. Defaults to the lowercased name of the associated model plus "_id", e.g. an association with a
* model called Product would set up a product_id foreign key.
*
* Ext.define('Order', {
* extend: 'Ext.data.Model',
* fields: ['id', 'date'],
* hasMany: 'Product'
* });
*
* Ext.define('Product', {
* extend: 'Ext.data.Model',
* fields: ['id', 'name', 'order_id'], // refers to the id of the order that this product belongs to
* belongsTo: 'Order'
* });
* var product = new Product({
* id: 1,
* name: 'Product 1',
* order_id: 22
* }, 1);
* product.getOrder(); // Will make a call to the server asking for order_id 22
*
*/
<span id='Ext-data-association-BelongsTo-cfg-getterName'> /**
</span> * @cfg {String} getterName The name of the getter function that will be added to the local model's prototype.
* Defaults to 'get' + the name of the foreign model, e.g. getCategory
*/
<span id='Ext-data-association-BelongsTo-cfg-setterName'> /**
</span> * @cfg {String} setterName The name of the setter function that will be added to the local model's prototype.
* Defaults to 'set' + the name of the foreign model, e.g. setCategory
*/
<span id='Ext-data-association-BelongsTo-cfg-type'> /**
</span> * @cfg {String} type The type configuration can be used when creating associations using a configuration object.
* Use 'belongsTo' to create a BelongsTo association.
*
* associations: [{
* type: 'belongsTo',
* model: 'User'
* }]
*/
constructor: function(config) {
this.callParent(arguments);
var me = this,
ownerProto = me.ownerModel.prototype,
associatedName = me.associatedName,
getterName = me.getterName || 'get' + associatedName,
setterName = me.setterName || 'set' + associatedName;
Ext.applyIf(me, {
name : associatedName,
foreignKey : associatedName.toLowerCase() + "_id",
instanceName: associatedName + 'BelongsToInstance',
associationKey: associatedName.toLowerCase()
});
ownerProto[getterName] = me.createGetter();
ownerProto[setterName] = me.createSetter();
},
<span id='Ext-data-association-BelongsTo-method-createSetter'> /**
</span> * @private
* Returns a setter function to be placed on the owner model's prototype
* @return {Function} The setter function
*/
createSetter: function() {
var me = this,
foreignKey = me.foreignKey;
//'this' refers to the Model instance inside this function
return function(value, options, scope) {
// If we pass in an instance, pull the id out
if (value && value.isModel) {
value = value.getId();
}
this.set(foreignKey, value);
if (Ext.isFunction(options)) {
options = {
callback: options,
scope: scope || this
};
}
if (Ext.isObject(options)) {
return this.save(options);
}
};
},
<span id='Ext-data-association-BelongsTo-method-createGetter'> /**
</span> * @private
* Returns a getter function to be placed on the owner model's prototype. We cache the loaded instance
* the first time it is loaded so that subsequent calls to the getter always receive the same reference.
* @return {Function} The getter function
*/
createGetter: function() {
var me = this,
associatedName = me.associatedName,
associatedModel = me.associatedModel,
foreignKey = me.foreignKey,
primaryKey = me.primaryKey,
instanceName = me.instanceName;
//'this' refers to the Model instance inside this function
return function(options, scope) {
options = options || {};
var model = this,
foreignKeyId = model.get(foreignKey),
success,
instance,
args;
if (options.reload === true || model[instanceName] === undefined) {
instance = Ext.ModelManager.create({}, associatedName);
instance.set(primaryKey, foreignKeyId);
if (typeof options == 'function') {
options = {
callback: options,
scope: scope || model
};
}
// Overwrite the success handler so we can assign the current instance
success = options.success;
options.success = function(rec){
model[instanceName] = rec;
if (success) {
success.apply(this, arguments);
}
};
associatedModel.load(foreignKeyId, options);
// assign temporarily while we wait for data to return
model[instanceName] = instance;
return instance;
} else {
instance = model[instanceName];
args = [instance];
scope = scope || options.scope || model;
//TODO: We're duplicating the callback invokation code that the instance.load() call above
//makes here - ought to be able to normalize this - perhaps by caching at the Model.load layer
//instead of the association layer.
Ext.callback(options, scope, args);
Ext.callback(options.success, scope, args);
Ext.callback(options.failure, scope, args);
Ext.callback(options.callback, scope, args);
return instance;
}
};
},
<span id='Ext-data-association-BelongsTo-method-read'> /**
</span> * Read associated data
* @private
* @param {Ext.data.Model} record The record we're writing to
* @param {Ext.data.reader.Reader} reader The reader for the associated model
* @param {Object} associationData The raw associated data
*/
read: function(record, reader, associationData){
record[this.instanceName] = reader.read([associationData]).records[0];
}
});
</pre>
</body>
</html>