Skip to content

Plugin: Virtuals

Ben Drucker edited this page Aug 18, 2014 · 2 revisions

Introduction

Sometimes you might want to compute additional values on your model based on other values. Maybe you want to generate a fullName property, based on a firstName and lastName? The virtuals plugin helps you to easily achieve this type of behavior.

How to use

First load the plugin using Bookshelf.plugin('virtuals'). Now you are all set to define virtuals on your models.

Defining virtuals

Defining virtuals is very easy. Let's stick with the example of generating a fullName property from a firstName and lastName property. At first we define a model, which has a special property virtuals:

var ModelWithVirtuals = bookshelf.Model.extend({
  virtuals: {
    fullName: function() {
        return this.get('firstName') + ' ' + this.get('lastName');
    }
  }
});

The above code will will generate a virtual property on your extended model, which can be accessed in two ways:

var instance = new ModelWithVirtuals({firstName: 'Joe', lastName: 'Shmoe'});

instance.fullName // => 'Joe Shmoe'
instance.get('fullName') // => 'Joe Shmoe'

This virtual property is a simple getter. If you want to also use it to set properties, you can define it like this:

var ModelWithVirtuals = bookshelf.Model.extend({
  virtuals: {
    fullName: {
      get: function () {
        return this.get('firstName') + ' ' + this.get('lastName');
      },
      set: function(value) {
        value = value.split(' ');
        this.set('firstName', value[0]);
        this.set('lastName', value[1]);
      }
    }
  }
});

If a setter is not defined, set calls will be noops. When defining a virtual with a get and a set property, it can be used in the following way:

var instance = new ModelWithVirtuals({firstName: 'Joe', lastName: 'Shmoe'});
instance.fullName // => 'Joe Shmoe'

// now we set a new value
instance.fullName = 'Jack Shmoe';

instance.fullName // => 'Jack Shmoe'
instance.get('firstName') // => 'Jack'
instance.get('lastName') // => 'Shmoe'

instance.set('fullName', 'John Doe');

instance.fullName // => 'John Doe'
instance.get('firstName') // => 'John'
instance.get('lastName') // => 'Doe'

This can be a very powerful feature, if used correctly.

By default all virtual properties are included when you convert your model to json. If you do not want them to be included you can turn inclusion off, by passing outputVirtuals: false when extending the model. This will override the default behavior.

But you do not have to always change the default behavior, if you want to include or exclude them on a per case basis, then that's fine too! You can pass an options hash to the toJSON method of the model, which will always override the default behavior:

var ModelWithVirtuals = bookshelf.Model.extend({
  outputVirtuals: false
  virtuals: {
    fullName: function() {
        return this.get('firstName') + ' ' + this.get('lastName');
    }
  }
});

var instance = new ModelWithVirtuals({firstName: 'Joe', lastName: 'Shmoe'});

instance.toJSON() // => {"firstName: "Joe", "lastName": "Shmoe"}
instance.toJSON({virtuals: true}) // => {"firstName: "Joe", "lastName": "Shmoe", "fullName": "Joe Shmoe"}