Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Always fetch from related models in Bookshelf.js

I would like baffle.where({id: 1}).fetch() to always get typeName attribute as a part of baffle model, without fetching it from baffleType explicitly each time.

The following works for me but it seems that withRelated will load relations if baffle model is fetched directly, not by relation:

let baffle = bookshelf.Model.extend({
    constructor: function() {
        bookshelf.Model.apply(this, arguments);

        this.on('fetching', function(model, attrs, options) {
            options.withRelated = options.withRelated || [];
            options.withRelated.push('type');           
        });
    },

    virtuals: {
        typeName: {
            get: function () {
                return this.related('type').attributes.typeName;
            }
        }
    },
    type: function () {
        return this.belongsTo(baffleType, 'type_id');
    }
});

let baffleType = bookshelf.Model.extend({});

What is the proper way to do that?

like image 682
Estus Flask Avatar asked Feb 28 '16 07:02

Estus Flask


2 Answers

Issue on repo is related to Fetched event, However Fetching event is working fine (v0.9.2).

So just for example if you have a 3rd model like

var Test = Bookshelf.model.extend({
   tableName : 'test',
   baffleField : function(){
       return this.belongsTo(baffle)
   } 
})

and then do Test.forge().fetch({ withRelated : ['baffleField']}), fetching event on baffle will fire. However ORM will not include type (sub Related model) unless you specifically tell it to do so by

Test.forge().fetch({ withRelated : ['baffleField.type']})

However I would try to avoid this if it is making N Query for N records.

UPDATE 1

I was talking about same thing that you were doing on fetching event like

fetch: function fetch(options) {
   var options = options || {}
   options.withRelated = options.withRelated || [];
   options.withRelated.push('type');    
    // Fetch uses all set attributes.
   return this._doFetch(this.attributes, options);
}

in model.extend. However as you can see, this might fail on version changes.

like image 60
Satyajeet Avatar answered Nov 20 '22 20:11

Satyajeet


This question is super old, but I'm answering anyway.

I solved this by just adding a new function, fetchFull, which keeps things pretty DRY.

let MyBaseModel = bookshelf.Model.extend({
  fetchFull: function() {
    let args;
    if (this.constructor.withRelated) {
      args = {withRelated: this.constructor.withRelated};
    }
    return this.fetch(args);
  },
};

let MyModel = MyBaseModel.extend({
    tableName: 'whatever',
  }, {
    withRelated: [
      'relation1',
      'relation1.related2'
    ]
  }
);

Then whenever you're querying, you can either call Model.fetchFull() to load everything, or in cases where you don't want to take a performance hit, you can still resort to Model.fetch().

like image 39
JonG Avatar answered Nov 20 '22 20:11

JonG