]>
git.proxmox.com Git - sencha-touch.git/blob - src/src/data/association/HasOne.js
2 * Represents a one to one association with another model. The owner model is expected to have
3 * a foreign key which references the primary key of the associated model:
5 * Ext.define('Person', {
6 * extend: 'Ext.data.Model',
9 * { name: 'id', type: 'int' },
10 * { name: 'name', type: 'string' },
11 * { name: 'address_id', type: 'int'}
14 * // we can use the hasOne shortcut on the model to create a hasOne association
22 * Ext.define('Address', {
23 * extend: 'Ext.data.Model',
26 * { name: 'id', type: 'int' },
27 * { name: 'number', type: 'string' },
28 * { name: 'street', type: 'string' },
29 * { name: 'city', type: 'string' },
30 * { name: 'zip', type: 'string' }
35 * In the example above we have created models for People and Addresses, and linked them together
36 * by saying that each Person has a single Address. This automatically links each Person to an Address
37 * based on the Persons address_id, and provides new functions on the Person model:
39 * ## Generated getter function
41 * The first function that is added to the owner model is a getter function:
43 * var person = Ext.create('Person', {
49 * person.getAddress(function(address, operation) {
50 * // do something with the address object
51 * alert(address.get('id')); // alerts 20
54 * The getAddress function was created on the Person model when we defined the association. This uses the
55 * Persons configured {@link Ext.data.proxy.Proxy proxy} to load the Address asynchronously, calling the provided
56 * callback when it has loaded.
58 * The new getAddress function will also accept an object containing success, failure and callback properties
59 * - callback will always be called, success will only be called if the associated model was loaded successfully
60 * and failure will only be called if the associated model could not be loaded:
63 * reload: true, // force a reload if the owner model is already cached
64 * callback: function(address, operation) {}, // a function that will always be called
65 * success : function(address, operation) {}, // a function that will only be called if the load succeeded
66 * failure : function(address, operation) {}, // a function that will only be called if the load did not succeed
67 * scope : this // optionally pass in a scope object to execute the callbacks in
70 * In each case above the callbacks are called with two arguments - the associated model instance and the
71 * {@link Ext.data.Operation operation} object that was executed to load that instance. The Operation object is
72 * useful when the instance could not be loaded.
74 * Once the getter has been called on the model, it will be cached if the getter is called a second time. To
75 * force the model to reload, specify reload: true in the options object.
77 * ## Generated setter function
79 * The second generated function sets the associated model instance - if only a single argument is passed to
80 * the setter then the following two calls are identical:
83 * person.setAddress(10);
85 * // is equivalent to this call:
86 * person.set('address_id', 10);
88 * An instance of the owner model can also be passed as a parameter.
90 * If we pass in a second argument, the model will be automatically saved and the second argument passed to
91 * the owner model's {@link Ext.data.Model#save save} method:
93 * person.setAddress(10, function(address, operation) {
94 * // the address has been saved
95 * alert(address.get('address_id')); //now alerts 10
98 * //alternative syntax:
99 * person.setAddress(10, {
100 * callback: function(address, operation) {}, // a function that will always be called
101 * success : function(address, operation) {}, // a function that will only be called if the load succeeded
102 * failure : function(address, operation) {}, // a function that will only be called if the load did not succeed
103 * scope : this //optionally pass in a scope object to execute the callbacks in
108 * Associations reflect on the models they are linking to automatically set up properties such as the
109 * {@link #primaryKey} and {@link #foreignKey}. These can alternatively be specified:
111 * Ext.define('Person', {
112 * extend: 'Ext.data.Model',
120 * primaryKey: 'unique_id',
121 * foreignKey: 'addr_id'
126 * Here we replaced the default primary key (defaults to 'id') and foreign key (calculated as 'address_id')
127 * with our own settings. Usually this will not be needed.
130 * [Sencha Touch Models and Associations](../../../core_concepts/data/models.html)
132 Ext
.define('Ext.data.association.HasOne', {
133 extend
: 'Ext.data.association.Association',
134 alternateClassName
: 'Ext.data.HasOneAssociation',
136 alias
: 'association.hasone',
140 * @cfg {String} foreignKey The name of the foreign key on the owner model that links it to the associated
141 * model. Defaults to the lowercased name of the associated model plus "_id", e.g. an association with a
142 * model called Person would set up a address_id foreign key.
144 * Ext.define('Person', {
145 * extend: 'Ext.data.Model',
146 * fields: ['id', 'name', 'address_id'], // refers to the id of the address object
150 * Ext.define('Address', {
151 * extend: 'Ext.data.Model',
152 * fields: ['id', 'number', 'street', 'city', 'zip'],
153 * belongsTo: 'Person'
155 * var Person = new Person({
157 * name: 'John Smith',
160 * person.getAddress(); // Will make a call to the server asking for address_id 13
163 foreignKey
: undefined,
166 * @cfg {String} getterName The name of the getter function that will be added to the local model's prototype.
167 * Defaults to 'get' + the name of the foreign model, e.g. getAddress
169 getterName
: undefined,
172 * @cfg {String} setterName The name of the setter function that will be added to the local model's prototype.
173 * Defaults to 'set' + the name of the foreign model, e.g. setAddress
175 setterName
: undefined,
177 instanceName
: undefined
180 applyForeignKey: function(foreignKey
) {
182 var inverse
= this.getInverseAssociation();
184 foreignKey
= inverse
.getForeignKey();
186 foreignKey
= this.getAssociatedName().toLowerCase() + '_id';
192 updateForeignKey: function(foreignKey
, oldForeignKey
) {
193 var fields
= this.getOwnerModel().getFields(),
194 field
= fields
.get(foreignKey
);
197 field
= new Ext
.data
.Field({
201 fields
.isDirty
= true;
205 field
= fields
.get(oldForeignKey
);
207 fields
.remove(field
);
208 fields
.isDirty
= true;
213 applyInstanceName: function(instanceName
) {
215 instanceName
= this.getAssociatedName() + 'HasOneInstance';
220 applyAssociationKey: function(associationKey
) {
221 if (!associationKey
) {
222 var associatedName
= this.getAssociatedName();
223 associationKey
= associatedName
[0].toLowerCase() + associatedName
.slice(1);
225 return associationKey
;
228 applyGetterName: function(getterName
) {
230 var associatedName
= this.getAssociatedName();
231 getterName
= 'get' + associatedName
[0].toUpperCase() + associatedName
.slice(1);
236 applySetterName: function(setterName
) {
238 var associatedName
= this.getAssociatedName();
239 setterName
= 'set' + associatedName
[0].toUpperCase() + associatedName
.slice(1);
244 updateGetterName: function(getterName
, oldGetterName
) {
245 var ownerProto
= this.getOwnerModel().prototype;
247 delete ownerProto
[oldGetterName
];
250 ownerProto
[getterName
] = this.createGetter();
254 updateSetterName: function(setterName
, oldSetterName
) {
255 var ownerProto
= this.getOwnerModel().prototype;
257 delete ownerProto
[oldSetterName
];
260 ownerProto
[setterName
] = this.createSetter();
266 * Returns a setter function to be placed on the owner model's prototype
267 * @return {Function} The setter function
269 createSetter: function() {
271 foreignKey
= me
.getForeignKey(),
272 instanceName
= me
.getInstanceName(),
273 associatedModel
= me
.getAssociatedModel();
275 //'this' refers to the Model instance inside this function
276 return function(value
, options
, scope
) {
277 var Model
= Ext
.data
.Model
,
280 if (value
&& value
.isModel
) {
281 value
= value
.getId();
284 this.set(foreignKey
, value
);
286 if (value
|| value
=== 0) {
287 record
= Model
.cache
[Model
.generateCacheId(associatedModel
.modelName
, value
)];
289 this[instanceName
] = record
;
292 delete this[instanceName
];
295 if (Ext
.isFunction(options
)) {
302 if (Ext
.isObject(options
)) {
303 return this.save(options
);
312 * Returns a getter function to be placed on the owner model's prototype. We cache the loaded instance
313 * the first time it is loaded so that subsequent calls to the getter always receive the same reference.
314 * @return {Function} The getter function
316 createGetter: function() {
318 associatedModel
= me
.getAssociatedModel(),
319 foreignKey
= me
.getForeignKey(),
320 instanceName
= me
.getInstanceName();
322 //'this' refers to the Model instance inside this function
323 return function(options
, scope
) {
324 options
= options
|| {};
327 foreignKeyId
= model
.get(foreignKey
),
328 success
, instance
, args
;
330 if (options
.reload
=== true || model
[instanceName
] === undefined) {
331 if (typeof options
== 'function') {
334 scope
: scope
|| model
338 // Overwrite the success handler so we can assign the current instance
339 success
= options
.success
;
340 options
.success = function(rec
){
341 model
[instanceName
] = rec
;
343 success
.apply(this, arguments
);
347 associatedModel
.load(foreignKeyId
, options
);
349 instance
= model
[instanceName
];
351 scope
= scope
|| model
;
353 Ext
.callback(options
, scope
, args
);
354 Ext
.callback(options
.success
, scope
, args
);
355 Ext
.callback(options
.failure
, scope
, args
);
356 Ext
.callback(options
.callback
, scope
, args
);
364 * Read associated data
366 * @param {Ext.data.Model} record The record we're writing to
367 * @param {Ext.data.reader.Reader} reader The reader for the associated model
368 * @param {Object} associationData The raw associated data
370 read: function(record
, reader
, associationData
) {
371 var inverse
= this.getInverseAssociation(),
372 newRecord
= reader
.read([associationData
]).getRecords()[0];
374 record
[this.getSetterName()].call(record
, newRecord
);
376 //if the inverse association was found, set it now on each record we've just created
378 newRecord
[inverse
.getInstanceName()] = record
;
382 getInverseAssociation: function() {
383 var ownerName
= this.getOwnerModel().modelName
;
385 return this.getAssociatedModel().associations
.findBy(function(assoc
) {
386 return assoc
.getType().toLowerCase() === 'belongsto' && assoc
.getAssociatedModel().modelName
=== ownerName
;