Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a Many-to-Many relationship with Backbone-Relational

I have a simple application which defines two classes, a Person and a PersonGroup, wherein there is a many-to-many relationship in place. A Person can have no group, or be assigned to all groups, and anything in between.

The example on backbonerelational.org suggests using an in-between model for many-to-many relationships, however I can't get this pattern to work with fetching (deserializing) and saving (serializing).

What I want to do is use Backbone to deserialize a JSON similar to the following:

{
    People:
    [
        {
            "ID": 1,
            "Name": "John"
        },
        {
            "ID": 2,
            "Name": "Bob"
        },
        {
            "ID": 3,
            "Name": "Tim"
        },
    ],
    PeopleGroups:
    [
        {
            "ID": 1,
            "Name": "Owners",
            "People":
            [
                1,
                2
            ],
        },
        {
            "ID": 2,
            "Name": "Everyone",
            "People":
            [
                1,
                2,
                3
            ],
        },
    ]
}

I'm using Knockback/Knockout for data binding so the problem is I need to be able to access the relationships by reference. An array of IDs does not do me any good, unless I can create a Knockback.CollectionObservable to wrap the collection and resolve the IDs to references.

like image 241
Trevor Elliott Avatar asked Jul 23 '13 13:07

Trevor Elliott


2 Answers

I ended up getting this to work the way I wanted to. The Many-to-Many relationship is maintained in the database but the relationships can only be accessed in my Backbone models in one direction. I decided that the Person models would be stand-alone and the PersonGroup models would have a collection of references to the Person models they are linked to.

The key points here that made everything work were to specify includeInJSON: "ID" and to remove the reverseRelation. This still lets you access the references to the models in JavaScript, but it correctly serializes and deserializes to JSON. The Person models simply don't have access to a navigation property to the Groups they are in, however they can exist in multiple groups just fine.

I simply assumed that using a list of IDs would mandate jumping through hoops to resolve references, but Backbone-relational seems to use the global Backbone model store to resolve references by ID without creating duplicate models. (eg. Three different groups can reference the same Person and only one model is ever created).

var Person = Backbone.RelationalModel.extend(
{
    idAttribute: "ID",
});

var PersonGroup = Backbone.RelationalModel.extend(
{
    idAttribute: "ID",

    relations:
    [
        {
            type: Backbone.HasMany,
            key: "People",
            relatedModel: "Person",
            collectionType: "PersonCollection",
            includeInJSON: "ID",
        },
    ],
});

And here's the rest of the plumbing if it helps anyone.You can define the Backbone.Collections as follows and obtain them through separate API requests:

var PersonCollection = Backbone.Collection.extend(
{
    model: Person,

    url: "/api/person",
});

var PersonGroupCollection = Backbone.Collection.extend(
{
    model: PersonGroup,

    url: "/api/persongroup",
});

var PersonModels = new PersonCollection();

var GroupsModels = new PersonGroupCollection();

this.PersonModels.fetch();

this.GroupsModels.fetch();

this.People = kb.collectionObservable(
    this.PersonModels,
    {
        factories:
        {
            "models": PersonViewModel,
        },
    }
);

this.PersonGroups = kb.collectionObservable(
    this.GroupsModels,
    {
        factories:
        {
            "models": PersonGroupViewModel,
            "models.People.models": PersonViewModel,
        },
    }
);

I included the Knockback.js specific collections for using Knockout.js bindings. Only one ViewModel is created per model so change tracking is propagated through the entire app.

like image 196
Trevor Elliott Avatar answered Sep 30 '22 20:09

Trevor Elliott


This question is about two months old, nevertheless I would like to share that I ran into similar problems concerning many-to-many relationships when working with Backbone-relational. These problems led to the writing of an own implementation of Backbone relations which support many-to-many relationships without a linking model in between: Backbone-JJRelational

I am answering as one of the authors, but it might be worth to check out.
The JSON you posted in your question, for example, would automatically be deserialized in the following way:
Each PeopleGroups-model has an attribute People which is a Backbone.Collection containing the appropriate People-models and (of course) vice-versa, so the relation can be easily accessed from both sides (without a linking model).
Some concepts of Backbone-JJRelational are similar to Backbone-relational (global model store, for example).

like image 25
johnnycrab Avatar answered Sep 30 '22 19:09

johnnycrab