PDA

View Full Version : [CLOSED] Model getData does not handle more association levels



m_laci
19 Dec 2012, 5:42 AM
REQUIRED INFORMATION
Ext version tested:

Ext 4.1.3 Build date: 2012-10-25 15:13:53 (240477695016a85fb9ed1098fd5f8e116327fcc3)
Browser versions tested against:

Chrome 23.0.1271.64
IE 10.0.9200
Description:

When using the function getData(true) on a model that has more than one level association depth, the function only returns first level
Is this how is intended to work, or should return parent.child.child... data also?
Steps to reproduce the problem:

Create a model class that more than one association depth level (User --> Order --> Product)
Call .getData(true)
Check if the returned object contains data on more than one level
The result that was expected:

Returns data for the first depth level of associations (User --> Order)
The result that was not expected

Does not returns the data on depth levels > 1 (User --> Order --> Product)
Test Case:


/*global Ext, U4, window */
Ext.require([
'Ext.data.Model',
'Ext.data.Store',
'Ext.data.proxy.Memory'
], function () {
describe('Ext.data.Model', function () {
Ext.define('Tests.Model', {
extend: 'Ext.data.Model',
fields: [
{ name: 'name', type: 'string' }
],
idProperty: 'name',
hasMany: {
model: 'Tests.Model',
name: 'children'
}
});


Ext.define('Tests.ChangedModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'name', type: 'string' }
],
idProperty: 'name',
hasMany: {
model: 'Tests.ChangedModel',
name: 'children'
},
prepareAssociatedData: function(seenKeys, depth) {
/*
* In this method we use a breadth first strategy instead of depth
* first. The reason for doing so is that it prevents messy & difficult
* issues when figuring out which associations we've already processed
* & at what depths.
*/
var me = this,
associations = me.associations.items,
associationCount = associations.length,
associationData = {},
// We keep 3 lists at the same index instead of using an array of objects.
// The reasoning behind this is that this method gets called a lot
// So we want to minimize the amount of objects we create for GC.
toRead = [],
toReadKey = [],
toReadIndex = [],
associatedStore, associatedRecords, associatedRecord, o, index, result, seenDepth,
associationId, associatedRecordCount, association, i, j, type, name;


for (i = 0; i < associationCount; i++) {
association = associations[i];
associationId = association.associationId;


seenDepth = seenKeys[associationId];
if (seenDepth && seenDepth !== depth) {
continue;
}
seenKeys[associationId] = depth;


type = association.type;
name = association.name;
if (type == 'hasMany') {
//this is the hasMany store filled with the associated data
associatedStore = me[association.storeName];


//we will use this to contain each associated record's data
associationData[name] = [];


//if it's loaded, put it into the association data
if (associatedStore && associatedStore.getCount() > 0) {
associatedRecords = associatedStore.data.items;
associatedRecordCount = associatedRecords.length;


//now we're finally iterating over the records in the association. Get
// all the records so we can process them
for (j = 0; j < associatedRecordCount; j++) {
associatedRecord = associatedRecords[j];
associationData[name][j] = associatedRecord.getData(true);
toRead.push(associatedRecord);
toReadKey.push(name);
toReadIndex.push(j);
}
}
} else if (type == 'belongsTo' || type == 'hasOne') {
associatedRecord = me[association.instanceName];
// If we have a record, put it onto our list
if (associatedRecord !== undefined) {
associationData[name] = associatedRecord.getData(true);
toRead.push(associatedRecord);
toReadKey.push(name);
toReadIndex.push(-1);
}
}
}


for (i = 0, associatedRecordCount = toRead.length; i < associatedRecordCount; ++i) {
associatedRecord = toRead[i];
o = associationData[toReadKey[i]];
index = toReadIndex[i];
result = associatedRecord.prepareAssociatedData(seenKeys, depth + 1);
if (index === -1) {
Ext.apply(o, result);
} else {
Ext.apply(o[index], result);
}
}


return associationData;
}
});


function createTestData(data, name, maxDepth, currentDepth) {
var children,
child,
index;
data.name = name;
if (currentDepth !== maxDepth) {
children = [];
for (index = 0; index !== currentDepth; index += 1) {
child = {};
createTestData(child, name + "_" + currentDepth + '_' + index, maxDepth, currentDepth + 1);
children.push(child);
}
data.children = children;
}
}


function createModel(modelName, name, maxDepth) {
var store = Ext.create('Ext.data.Store', {
model: modelName
}),
memoryProxy,
model,
testData = {};
createTestData(testData, name, maxDepth, 1);
memoryProxy = Ext.create('Ext.data.proxy.Memory', { data: testData });
store.setProxy(memoryProxy);
store.load();
model = store.getAt(0);
store.destroy();
return model;
}




it('getData should handle more association depth levels', function () {
var model = createModel('Tests.Model', 'Test1', 3),
data = model.getData(true);
expect(model.childrenStore.getCount()).toBe(data.children.length); // passed
expect(model.childrenStore.getAt(0).childrenStore.getCount()).toBe(
(data.children[0] && data.children[0].children) ? data.children.children.length : -1
);// failed
});


it('changed getData handles more association depth level', function () {
var model = createModel('Tests.ChangedModel', 'Test1', 3),
data = model.getData(true);
expect(model.childrenStore.getCount()).toBe(data.children.length); // passed
expect(model.childrenStore.getAt(0).childrenStore.getCount()).toBe(data.children[0].children.length);// passed
expect(model.childrenStore.getAt(0).childrenStore.getAt(0).name === data.children[0].children[0].name);
});


});
});

Possible fix:

Please see code from Tests.ChangedModel
Additional CSS used:

only default ext-all.css
Operating System:

Windows 7 Enterprise

mitchellsimoens
19 Dec 2012, 7:41 AM
Thanks for the report! I have opened a bug in our bug tracker.