/** * This type of association is similar to {@link Ext.data.schema.ManyToOne many-to-one}, * except that the {@link Ext.data.field.Field#cfg-reference reference} field also has set * {@link Ext.data.field.Field#cfg-unique unique} to `true`. * * While this type of association helps handle both sides of the association properly, it * is problematic to enforce the uniqueness aspect. If the database were to enforce this * uniqueness constraint, it would limit the field to be non-nullable. Even if this were * acceptable, this also creates challenges for a "soft-delete" strategy where records are * kept in the table, but only marked as "deleted" in a field. * * Ensuring uniqueness on the client-side is also difficult. So, at the present time, this * is not enforced. */Ext.define('Ext.data.schema.OneToOne', { extend: 'Ext.data.schema.Association', isOneToOne: true, isToOne: true, kind: 'one-to-one', Left: Ext.define(null, { extend: 'Ext.data.schema.Role', onDrop: function(rightRecord, session) { rightRecord[this.role] = null; }, createGetter: function() { var me = this; return function () { // 'this' refers to the Model instance inside this function return me.doGet(this); }; }, createSetter: function () { var me = this; return function (value) { // 'this' refers to the Model instance inside this function return me.doSet(this, value); }; }, doGet: function (rightRecord) { // Consider the Department entity with a managerId to a User entity. The // Department is on the left (the FK holder's side) so we are implementing the // guts of the getManagerDepartment method we place on the User entity. Since // we represent the "managerDepartment" role and as such our goal is to get a // Department instance, we start that from the User (rightRecord). Sadly that // record has no FK back to us. var propertyName = this.role, // ex "managerDepartment" ret = rightRecord[propertyName], session = rightRecord.session; if (!ret && session) { // @TODO: session - we'll cache the result on the record as always // but to get it we must ask the session } return ret || null; }, doSet: function (rightRecord, leftRecord) { // We are the guts of the setManagerDepartment method we place on the User // entity. Our goal here is to establish the relationship between the new // Department (leftRecord) and the User (rightRecord). var propertyName = this.role, // ex "managerDepartment" ret = rightRecord[propertyName], inverseSetter = this.inverse.setterName; // setManager for Department if (ret !== leftRecord) { rightRecord[propertyName] = leftRecord; if (inverseSetter) { // Because the FK is owned by the inverse record, we delegate the // majority of work to its setter. We've already locked in the only // thing we keep on this side so we won't recurse back-and-forth. leftRecord[inverseSetter](rightRecord); } } return ret; }, read: function(rightRecord, node, fromReader, readOptions) { var me = this, result = me.callParent([ rightRecord, node, fromReader, readOptions ]), leftRecord = result.getRecords()[0], name = me.role; if (leftRecord) { leftRecord[me.inverse.role] = rightRecord; rightRecord[name] = leftRecord; // Inline associations should *not* arrive on the "data" object: delete rightRecord.data[name]; } } }), Right: Ext.define(null, { extend: 'Ext.data.schema.Role', left: false, side: 'right', createGetter: function() { // As the target of the FK (say "manager" for the Department entity) this // getter is responsible for getting the entity referenced by the FK value. var me = this; return function (options, scope) { // 'this' refers to the Model instance inside this function return me.doGetFK(this, options, scope); }; }, createSetter: function() { var me = this; return function(value, options, scope) { // 'this' refers to the Model instance inside this function return me.doSetFK(this, value, options, scope); }; }, onDrop: function(leftRecord, session) { var me = this, field = me.association.field, rightRecord, id; if (me.inverse.owner) { if (session) { id = leftRecord.get(field.name); if (id || id === 0) { rightRecord = session.getEntry(me.cls, id).record; if (rightRecord) { rightRecord.drop(); } } } else { rightRecord = me.getAssociatedItem(leftRecord); if (rightRecord) { rightRecord.drop(); } } } if (field) { leftRecord.set(field.name, null); } leftRecord[me.role] = null; }, onValueChange: function(leftRecord, session, newValue) { // Important to get the record before changing the key. var me = this, rightRecord = me.getAssociatedItem(leftRecord); leftRecord.changingKey = true; me.doSetFK(leftRecord, newValue); if (me.inverse.owner && rightRecord) { me.association.schema.queueKeyCheck(rightRecord, me); } leftRecord.changingKey = false; }, checkKeyForDrop: function(rightRecord) { var leftRecord = this.inverse.getAssociatedItem(rightRecord); if (!leftRecord) { // Not reassigned to another parent rightRecord.drop(); } }, read: function(leftRecord, node, fromReader, readOptions) { var me = this, result = me.callParent([ leftRecord, node, fromReader, readOptions ]), rightRecord = result.getRecords()[0], name = me.role, field = this.association.field, session = leftRecord.session, oldId; if (rightRecord) { rightRecord[me.inverse.role] = leftRecord; leftRecord[name] = rightRecord; // Inline associations should *not* arrive on the "data" object: delete leftRecord.data[name]; // We want to poke the inferred key onto record if it exists, but we don't // want to mess with the dirty or modified state of the record. if (field) { oldId = leftRecord.data[field.name]; if (oldId !== rightRecord.id) { leftRecord.data[field.name] = rightRecord.id; if (session) { session.updateReference(leftRecord, field, rightRecord.id, oldId); } } } } } })});