blossom
Version:
Modern, Cross-Platform Application Framework
155 lines (123 loc) • 5.06 kB
JavaScript
// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2011 Strobe Inc. and contributors.
// Portions ©2008-2011 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
sc_require('models/record');
sc_require('models/record_attribute');
sc_require('system/many_array');
/** @class
ManyAttribute is a subclass of `RecordAttribute` and handles to-many
relationships.
When setting ( `.set()` ) the value of a `toMany` attribute, make sure
to pass in an array of `SC.Record` objects.
There are many ways you can configure a `ManyAttribute`:
contacts: SC.Record.toMany('MyApp.Contact', {
inverse: 'group', // set the key used to represent the inverse
isMaster: true|false, // indicate whether changing this should dirty
transform: function(), // transforms value <=> storeKey,
isEditable: true|false, make editable or not,
through: 'taggings' // set a relationship this goes through
});
@extends SC.RecordAttribute
@since SproutCore 1.0
*/
SC.ManyAttribute = SC.RecordAttribute.extend(
/** @scope SC.ManyAttribute.prototype */ {
isManyAttribute: true, // Walk like a duck.
/**
Set the foreign key on content objects that represent the inversion of
this relationship. The inverse property should be a `toOne()` or
`toMany()` relationship as well. Modifying this many array will modify
the `inverse` property as well.
@property {String}
*/
inverse: null,
/**
If `true` then modifying this relationships will mark the owner record
dirty. If set to `false`, then modifying this relationship will not alter
this record. You should use this property only if you have an inverse
property also set. Only one of the inverse relationships should be marked
as master so you can control which record should be committed.
@property {Boolean}
*/
isMaster: true,
/**
If set and you have an inverse relationship, will be used to determine the
order of an object when it is added to an array. You can pass a function
or an array of property keys.
@property {Function|Array}
*/
orderBy: null,
// ..........................................................
// LOW-LEVEL METHODS
//
/** @private - adapted for to many relationship */
toType: function(record, key, value) {
var type = this.get('typeClass'),
attrKey = this.get('key') || key,
arrayKey = SC.keyFor('__manyArray__', SC.guidFor(this)),
ret = record[arrayKey],
rel;
// lazily create a ManyArray one time. after that always return the
// same object.
if (!ret) {
ret = SC.ManyArray.create({
recordType: type,
record: record,
propertyName: attrKey,
manyAttribute: this
});
record[arrayKey] = ret ; // save on record
rel = record.get('relationships');
if (!rel) record.set('relationships', rel = []);
rel.push(ret); // make sure we get notified of changes...
}
return ret;
},
/** @private - adapted for to many relationship */
fromType: function(record, key, value) {
var ret = [];
if(!SC.isArray(value)) throw "Expects toMany attribute to be an array";
var len = value.get('length');
for(var i=0;i<len;i++) {
ret[i] = value.objectAt(i).get('id');
}
return ret;
},
/**
Called by an inverse relationship whenever the receiver is no longer part
of the relationship. If this matches the inverse setting of the attribute
then it will update itself accordingly.
You should never call this directly.
@param {SC.Record} the record owning this attribute
@param {String} key the key for this attribute
@param {SC.Record} inverseRecord record that was removed from inverse
@param {String} key key on inverse that was modified
@returns {void}
*/
inverseDidRemoveRecord: function(record, key, inverseRecord, inverseKey) {
var manyArray = record.get(key);
if (manyArray) {
manyArray.removeInverseRecord(inverseRecord);
}
},
/**
Called by an inverse relationship whenever the receiver is added to the
inverse relationship. This will set the value of this inverse record to
the new record.
You should never call this directly.
@param {SC.Record} the record owning this attribute
@param {String} key the key for this attribute
@param {SC.Record} inverseRecord record that was added to inverse
@param {String} key key on inverse that was modified
@returns {void}
*/
inverseDidAddRecord: function(record, key, inverseRecord, inverseKey) {
var manyArray = record.get(key);
if (manyArray) {
manyArray.addInverseRecord(inverseRecord);
}
}
});