Skip to content

Instantly share code, notes, and snippets.

@thomaslang
Created August 14, 2009 21:51
Show Gist options
  • Save thomaslang/168127 to your computer and use it in GitHub Desktop.
Save thomaslang/168127 to your computer and use it in GitHub Desktop.
// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
// Portions ©2008-2009 Apple, Inc. All rights reserved.
// License: Licened under MIT license (see license.js)
// ==========================================================================
//Set these require statements if put into frameworks/datastore/models:
//sc_require('models/record');
//sc_require('models/record_attribute');
/** @class
This is used to set up a 'to many' relationship from one model to another.
In contrast to the SC.Record.toMany relation, you don't have to store an
array of related records as a property on your model.
With this toManyByKey relation you can keep track of all records that have
a toOne relation to your record. You can do this either by giving a foreign
key on the related model or by specifying a complete query to find the
related records.
Additionally you can define in which order the related records should be.
This record attribute will give a record array of related records. This
array is computed by a query, will allways be up to date with your store
and is by its nature not editable. To declare two records related you will
have to use the toOne side of the relation.
For this to work, you need to define a corrresponding toOne attribute on
the SC.Record class the related records belong to.
Example usage:
To establish a bidirectional relationship between a user and a message model
you will have to put the following lines inside your model definitions:
{{{
MyApp.User = SC.Record.extend({
messages: SC.Record.toManyByKey('MyApp.Message', 'user')
})
MyApp.Message = SC.Record.Extend({
user: SC.Record.toOne('MyApp.User')
})
}}}
To define an order for the related records, use the orderBy option:
{{{
messages: SC.Record.toManyByKey('MyApp.Message', 'user',
{orderBy: 'priority DESC'})
}}}
If your related models aren't simply found by a foreign key you can use a
full SC.Query to establish the relationship instead of the foreignKey
(which must be NO in this case):
{{{
allMessages: SC.Record.toManyByKey('MyApp.Message', NO, {
conditions: 'sender = {user} OR reciever = {user}',
parameters: {user: this},
orderBy: 'priority DESC'
})
}}}
The first argument of the 'toManyByKey' function will be used as the
query's recordType property.
@author Thomas Langemann
@extends SC.RecordAttribute
@since SproutCore 1.0
*/
SC.ToManyRelation = SC.RecordAttribute.extend(
/** @scope SC.ToManyRelation.prototype */ {
foreignKey: null,
conditions: null,
parameters: null,
orderBy: null,
// ..........................................................
// LOW-LEVEL METHODS
//
/** @private - adapted for this toMany relationship */
toType: function(record, key, value) {
var conditions;
var parameters;
var foreignKey = this.get('foreignKey');
var orderBy = this.get('orderBy');
var recordType = this.get('typeClass');
var store = record.get('store');
if (foreignKey) {
conditions = foreignKey + " = %@";
parameters = [record];
}
else {
conditions = this.get('conditions');
parameters = this.get('parameters');
foreignKey = conditions; // used to create uniq cacheKey
}
var queryCacheKey = '__many_query__'+SC.guidFor(recordType)+"__"+foreignKey;
var query = record[queryCacheKey];
var recsCacheKey = '__many_records__'+SC.guidFor(recordType)+"__"+foreignKey;
var recs = record[recsCacheKey] ;
if (!query) {
query = record[queryCacheKey] = SC.Query.create({
recordType: recordType,
conditions: conditions,
parameters: parameters,
orderBy: orderBy
});
}
if (!recs) {
recs = record[recsCacheKey] = record.get('store').findAll(query);
}
return recs ;
}
});
// The following should go into datastore/models/record.js
SC.Record.mixin( /** @scope SC.Record */ {
/**
Helper method returns a new SC.ToManyRelation instance to establish a
to many relationship. You should pass the type class of the related
records and a foreign key that holds the id of this record.
Alternatively you can pass NO as foreignKey and provide conditions
and parameters in the options to create a SC.Query that will be used
to compute the array of related records.
Use this helper when you define SC.Record subclasses.
@param {Class} type the type of the related records
@param {String} foreignKey the foreign key on the related model
@param {Hash} opts the options for the relation
@returns {SC.RecordAttribute} created instance
*/
toManyByKey: function(type, foreignKey, opts) {
if (!opts) opts = {} ;
opts.type = type;
opts.foreignKey = foreignKey;
return SC.ToManyRelation.create(opts);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment