Backbone-relational
Backbone-relational copied to clipboard
Many to many for single model without linking model
I'm mapping out a Twitter like social graph with non-mutual followers/following relations. The use of a linking model makes sense in the Person/Job/Company example, but only adds overhead in the case of many-to-many for the same model on both sides. I find myself wanting to write a relation like
User = Backbone.RelationalModel.extend({
relations: [{
type: 'HasMany',
key: 'following',
relatedModel: 'User',
reverseRelation: {
type: 'HasMany',
key: 'followers'
}
}]
});
Unless there is reason for this, I'd much like this feature! (or might even implement it myself)
Well, the reason it isn't in there right now is that it's quite complicated to get this 'right', especially with regards to all the events flying around (which is already complicated as it is with just HasOne
and HasMany
relations). I've thought about it before, but didn't get to an intuitive / simple way to implement this. So I haven't until now; I'd rather have no implementation than a broken/buggy one.
I do have some ideas for an implementation, but it's not really fleshed out as of yet. I think it would basically have to be a new Backbone.Relation
type (Backbone.ManyToMany
?), which proxies two HasMany
relations. This relation should be responsible for keeping both HasMany
relations in sync, and relaying events to the other side of the relation. It could also allow through
accessors in a manner similar to Django/Rails?
I think this issue is pertinent to what I'm trying to do as well.
Basically I'm trying to create a simple file system which has a root folder which can then contain other folders or files, so I would need to allow a folder to folder relationship, with the added complication of having another model for files as well.
If I'm conceptualising this correctly I basically have the same problem that this functionality doesn't exist, yeah?
Completely understand why though, it's pretty difficult sounding coding problem :-)
i have the same problum
This would be brilliant for me. As it is now I create client-side through-models that have no correspondence on the server side, just to be able to do many2many. A django/rails equivalent behind-the-scens handling of those relations would be great!
I'm currently trying to solve this problem in my app and the conclusion I came to is that you can't really avoid not having a linking model.
In our app we have meeting
models which has an embedded invitees
collection and a function that returns an attributes()
collection, both of which consists of a users
collection with user
, with the additional attributes of rsvp_status
and inviter_ids
(the id's of users that did the inviting). After trying this approach, we learned that the moment we load up multiple meetings we have collisions with these two additional attributes because the model is relational, but these two attributes aren't. Our approach now is to change invitees
to an invites
collection of invite
models that has the user as a relational property.
The JSON was:
{
meeting: {
// other properties
invitees: [{
id: 123,
name: 'John Doe',
rsvp_status: 'attending',
inviter_ids: ['456', '789']
},{
id: 456,
name: 'Jane Doe',
rsvp_status: 'not_replied',
inviter_ids: ['789']
}]
}
}
And we changed it to:
{
meeting: {
// other properties
invites: [{
user: {
id: 123,
name: 'John Doe',
},
rsvp_status: 'attending',
inviter_ids: ['456', '789']
},{
user: {
id: 456,
name: 'Jane Doe',
},
rsvp_status: 'not_replied',
inviter_ids: ['789']
}]
}
}
The latter approach contains an embedded collection, which we create and bind change:invites events to to repopulate/reset the invitees and attendees collections. This way the meeting contains an embedded invite collection, which itself contains relational attributes referencing the user that received the invite and the users that did the inviting.
With this in mind, I'd love to see support for initializing embedded collections in the models relations
property, especially if I can define sieves
like in Backbone.Subset, where the sieve is a function that calculates whether someone should or should not be part of the collection. In my use case above, I'd have a relational invitees
collection whose members are determined by the users defined in the non-relational invites collection. I'd also have a relational attendees
collection whose members are those invitees whose invite.get('rsvp_status') is equal to attending
It seems that using the properties of an embedded "many to many linking" collection like invites
as a sieve to fill collection representing relations is one approach. It should be doable in a very "functional" way, where Backbone-Relational takes a function that returns the result of a truth test as a property in the relations objects.
I imagine something like this:
Meeting = Backbone.RelationalModel.extend({
// stuff
relations: [{
type: Backbone.HasMany,
key: 'invitees',
relatedModel: User,
collectionType: UserList,
sieve: this.resetInvitees,
reverseRelation: {
key: 'meetings',
relatedModel: Meeting,
includeInJSON: false
}
},{
type: Backbone.HasMany,
key: 'attendees',
relatedModel: User,
collectionType: UserList,
sieve: this.resetAttendees,
reverseRelation: {
key: 'meetings',
relatedModel: Meeting,
includeInJSON: false
}
}],
embedded: [{
type: Backbone.HasMany,
key: 'invites',
relatedModel: Invite,
collectionType: InviteList
}],
resetInvitees: function() {
var invitees = [];
this.invites.each(function(invite){
var user = invite.get('user');
invitees.push(user);
})
return invitees;
},
resetAttendees: function() {
var attendee = [];
this.invites.each(function(invite){
var user = invite.get('user');
if (user.get('rsvp_status') === 'attending') {
attendees.push(user);
}
})
return attendees;
},
// more stuff
});
Basically, in the above approach I use two function sieves to calculate which models from the embedded invites
linking collection to include in the relational collections invitees
and attendees
What I am not sure how to handle is the reverse relationships since the reverse relationships is not a property with a single meeting
model, but a one meeting
model in a collection of meeting
models, which is the reason I defined the reverseRelation like so:
reverseRelation: {
key: 'meetings',
relatedModel: Meeting,
includeInJSON: false
}
Any thoughts on this approach? Take a look at the Backbone.Subset plugin for a better understanding of what I have in mind. https://github.com/masylum/Backbone.Subset
@PaulUithol I'd love to here more about the Backbone.ManyToMany relationship approach you mentioned.
@emsten in your snippet above, where do you include the data that determines whether or not a model from collection A is linked to a model from collection B? Basically, using my case above, if Users can be invited to many meetings and meetings can contain many users, how do you determine which meetings a user is part of and which users are part of a meeting? The snippet shows that a many-to-many relationship between models of type A and type B are possible, but now how it is determined.
if there would be a many_many relation like here http://doc.silverstripe.org/sapphire/en/topics/datamodel#relations you were my hero!
Any updates on supporting this use case?
This feature would be dreamy. Any movement?
I tried adding support for a HasManyToMany relationship type today. It worked surprisingly well with very little effort. I'm probably missing some of the edge cases that @PaulUithol hinted at above. Does anyone have any insights into what issues I should be looking for?
Probably the best way forward would be for the community to come up with common patterns of handling this problem outside of Backbone-relational. As those patterns get refined and gain consensus, then parts of them could strategically be moved into Backbone-relational. Does anyone have good patterns to share on this topic? (They'd make great fodder for blogs!)