Search code examples
javascriptormrdbmsbookshelf.js

Inserting row into join table?


I have a many-to-many between Songs and People. The join model isn't specified because it only has three columns: primary key, song_id and person_id.

Bookshelf definitions:

//Song
Bookshelf.model("Song", {
    tableName: "songs",
    people: function() { 
        return this.belongsToMany("Person", "people_songs");
    }
});

//Person
Bookshelf.model("Person", {
    tableName: "people",
    songs: function() { 
        return this.belongsToMany("Song", "people_songs");
    }
});

Given a song id and person id, I want to add a row to the join table. Attach seems to be the correct method to use on the model instance, so in a controller I can do...

Models
    .Song
    .forge()
    .where({id: 1})
    .people()
    .attach( new Models.Person({id: 1}) )
    .then(function(instance) {
        res.send();
    });

When I query out the rows, song_id is null? (I should make the columns notNullable() but that's besides the point...)

I also tried updatePivot like this:

Models
    .Song
    .forge()
    .where({id: 1})
    .people()
    .updatePivot({song_id: 1, person_id: 1})
    .then(function(instance) {
        res.send();
    });

But that didn't even insert anything into the join table. However, if I log out the result of the updatePivot promise I get this:

relatedData: {
    targetTableName: "songs",
    targetIdAttribute: "id",
    joinTableName: "people_songs",
    foreignKey: "song_id"
    otherKey: undefined,
    parentId: undefined,
    parentTableName: "people",
    parentIdAttribute: "id",
    parentFk: undefined
}

Solution

  • Found a solution here in a repo issue.

    //get the model
    Models
        .Song
        .forge({id: 12})
        //but I have no idea why save is being used here instead of fetch
        .save()
        .then(function(model) {
            //get the other model you want to add to the join table
            return Models
                    .Person
                    .forge({id: 56})
                    .save()
                    .then(function(target) {
                        //need to send both references down the promise chain
                        return {target: target, model: model};
                    });
    
        })
        .then(function(references) {
            return references
                    .model
                    //get the belongsToMany relation specified in your Model definition, it returns a collection
                    .people()
                    //attach the target to the collection, not the model instance
                    .attach(references.target);
        })
        .then(function(relation) {
            res.send();
        })
    

    The result was

    people_songs
    
    id  |  person_id  | song_id
    ___________________________
    
    3   |  56         |  12