Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding collectionKey option to fix issue #31 #55

Merged
merged 8 commits into from
Dec 19, 2011
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ Value: a string (which can be resolved to an object type on the global scope), o

Determine the type of collections used for a `HasMany` relation. Defining a `url(models<Backbone.Model[]>)` function on this Collection that's able to build a url for either the whole collection, or a set of models enables `fetchRelated` to fetch all missing models in one request, instead of firing a separate request for each. See [Backbone-tastypie](https://github.com/PaulUithol/backbone-tastypie/blob/master/backbone-tastypie.js#L74) for an example.

### collectionKey

Value: a string or a boolean

By default, the relation's `key` attribute will be used to create a reference to the RelationalModel instance from the generated collection. If you set `collectionKey` to a string, it will use that string as the reference to the RelationalModel, rather than the relation's `key` attribute. If you don't want this behavior at all, just set `collectionKey` to false (or any falsy value) and this reference will not be created.

### includeInJSON

Value: a boolean, or a string referencing one of the model's attributes. Default: `true`.
Expand Down Expand Up @@ -202,6 +208,10 @@ ourHouse = new House({
// The first person in 'ourHouse.occupants' will point to 'paul'.
ourHouse.get('occupants').at(0); // === paul

// If a collection is created from a HasMany relation, it contains a reference
// back to the originator of the relation
ourHouse.get('occupants').livesIn; // === ourHouse

// the relation from 'House.occupants' to 'Person' has been defined as a bi-directional HasMany relation,
// with a reverse relation to 'Person.livesIn'. So, 'paul.livesIn' will automatically point back to 'ourHouse'.
paul.get('livesIn'); // === ourHouse
Expand Down Expand Up @@ -308,4 +318,4 @@ User = Backbone.RelationalModel.extend();

## <a name="under-the-hood"/>Under the hood

Each `Backbone.RelationalModel` registers itself with `Backbone.Store` upon creation (and is removed from the `Store` when destroyed). When creating or updating an attribute that is a key in a relation, removed related objects are notified of their removal, and new related objects are looked up in the `Store`.
Each `Backbone.RelationalModel` registers itself with `Backbone.Store` upon creation (and is removed from the `Store` when destroyed). When creating or updating an attribute that is a key in a relation, removed related objects are notified of their removal, and new related objects are looked up in the `Store`.
17 changes: 16 additions & 1 deletion backbone-relational.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,8 @@

options: {
reverseRelation: { type: 'HasOne' },
collectionType: Backbone.Collection
collectionType: Backbone.Collection,
collectionKey: true
},

initialize: function() {
Expand All @@ -625,6 +626,20 @@

collection.reset();
collection.model = this.relatedModel;
var collectionKey = this.options.collectionKey;
if (collectionKey) {
var key;
if (collectionKey === true) {
key = this.options.reverseRelation.key;
} else {
key = collectionKey;
}
if (collection[key] && collection[key] !== this.instance) {
Backbone.Relational.showWarnings && console && console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, collection );
} else {
collection[key] = this.instance;
}
}
collection.bind( 'relational:add', this.handleAddition ).bind('relational:remove', this.handleRemoval );
return collection;
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"keywords" : ["Backbone", "relation", "nested", "model"],
"repository" : "git://github.com/PaulUithol/Backbone-relational.git",
"author" : "Paul Uithol <https://github.com/PaulUithol>",
"contributors" : ["Vladimir Dronnikov <dronnikov@gmail.com>", "Jacob Henry (jachreny)", "Jens Alm (ulmus)"],
"contributors" : ["Vladimir Dronnikov <dronnikov@gmail.com>", "Jacob Henry (jachreny)", "Jens Alm (ulmus)", "David Baumgold (singingwolfboy)"],
"dependencies" : {
"underscore": ">=1.2.1",
"backbone": ">=0.5.3"
Expand Down
52 changes: 51 additions & 1 deletion test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1431,4 +1431,54 @@ $(document).ready(function() {
equals( zoo.get( 'name' ), 'Zoo Station' );
equals( lion.get( 'name' ), 'Simba' );
});
});

test("collectionKey attribute is used to create references on generated Collections back to its RelationalModel", function() {
var zoo = new Zoo({
animals: [ 'lion-1', 'zebra-1' ]
});

equals( zoo.get( 'animals' ).livesIn, zoo );
equals( zoo.get( 'animals' ).zoo, undefined );

Barn = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'animals',
relatedModel: 'Animal',
collectionType: 'AnimalCollection',
collectionKey: 'barn',
reverseRelation: {
key: 'livesIn',
includeInJSON: 'id'
}
}]
})
var barn = new Barn({
animals: [ 'chicken-1', 'cow-1' ]
});

equals( barn.get( 'animals' ).livesIn, undefined );
equals( barn.get( 'animals' ).barn, barn );

BarnNoKey = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'animals',
relatedModel: 'Animal',
collectionType: 'AnimalCollection',
collectionKey: false,
reverseRelation: {
key: 'livesIn',
includeInJSON: 'id'
}
}]
})
var barnNoKey = new BarnNoKey({
animals: [ 'chicken-1', 'cow-1' ]
});

equals( barnNoKey.get( 'animals' ).livesIn, undefined );
equals( barnNoKey.get( 'animals' ).barn, undefined );
});

});