Relations

Note

Exported from bookshelf-schema/lib/relations

Relations are used to declare relations between models.

When applied to model it will

  • create function that returns appropriate model or collection, like you normally does when define relations for bookshelf models
  • create accessor prefixed by ‘$’ symbol (may be configured)
  • may prevent destroying of parent model or react by cascade destroying of related models or detaching them

Examples

CoffeeScript

{HasMany} = require 'bookshelf-schema/lib/relations'

class Photo extends db.Model
  tableName: 'photos'

class User extends db.Model
  tableName: 'users'
  @schema [
    StringField 'username'
    HasMany Photo, onDestroy: 'cascade'               # [1]
  ]

User.forge(username: 'alice').fetch()
.then (alice) ->
  alice.load('photos')                                # [2]
.then (alice) ->
  alice.$photos.at(0).should.be.an.instanceof Photo   # [3]

JavaScript

var Relations = require('bookshelf-schema/lib/relations');
var HasMany = Relations.HasMany;

var Photo = db.Model.extend({ tableName: 'photos' });
var User = db.Model.extend({ tableName: 'users' }, {
  schema: [
    StringField('username'),
    HasMany(Photo, {onDestroy: 'cascade'})            // [1]
  ]
});

User.forge({username: 'alice'}).fetch()
.then( function(alice) {
  return alice.load('photos');                        // [2]
}).then ( function(alice) {
  alice.$photos.at(0).should.be.an.instanceof(Photo); // [3]
});
  • [1] HasMany will infer relation name from the name of related model and set it to ‘photos’

    When relation name is generated from model name it uses model name with lower first letter and pluralize it for multiple relations.

  • [1] when used with registry plugin you may use model name instead of class. It will be resolved in a lazy manner.

  • [2] load will work like in vanilla bookshelf thanks to auto-generated method ‘photos’

  • [3] $photos internally calls alice.related('photos') and returns fetched collection

Relation name

Actual relation name (the name of generated function) is generated from one of the following, sequently:

  • name passed as an option to relation constructor
  • string, passed as a relation if used with registry
  • relatedModel.name or relatedModel.displayName
  • camelized and singularized related table name

Additionally if name isn’t passed as an option relation name is pluralized for the multiple relations and its first letter is converted to lower case.

Accessor helper methods

In addition to common collection or model methods accessors provides several helpers:

assign(list, options = {})
Arguments:
  • list (Array) – list of related models, ids, or plain objects
  • options (Object) – options passed to save methods

alice.$photos.assign([ ... ])

Assigns passed objects to relation. All related models that doesn’t included to passed list will be detached. It will fetch passed ids and tries to creates new models for passed plain objects.

For singular relations such as HasOne or BelongsTo it accepts one object instead of list.

attach(list, options = {})

alice.$photos.attach([ ... ])

Similar to assign but only attaches objects.

detach(list, options = {}()

alice.$photos.detach([ ... ])

Similar to assign but only detaches objects. Obviously it can’t detach plain objects.

Note

assign, attach and detach are wrapped with transaction

Count

Collection.prototype.count()

Bookshelf Collection.prototype.count method is replaced and now (finally!) usable with relations and scoped collections. So you can do something like alice.$photos.count().then (photosCount) -> ...

And it still passes all the count-related tests provided by Bookshelf.

Base class

All relations are a subclass of Relation class.

class Relation(model, options = {})
Arguments:
  • model ((Class|String)) – related model class. Could be a string if used with registry plugin.
  • options (Object) – relation options

Options:

createProperty: Boolean, default true
create accessors for this relation
accessorPrefix: String, default “$”
used to generate name of accessor property
onDestroy: String, one of “ignore”, “cascade”, “reject”, “detach”, default “ignore”

determines what to do when parend model gets destroyed

  • ignore - do nothing
  • cascade - destroy related models
  • reject - prevent parent model destruction if there is related models
  • detach - detach related models first

Note

Model.destroy is patched so it will wrap callbacks and actual model destroy with transaction

through: (Class|String)
generate “through” relation
foreignKey, otherKey, foreignKeyTarget, otherKeyTarget, throughForeignKey, throughForeignKeyTarget: String
has the same meaning as in appropriate Bookshelf relations

Relation classes

HasOne

class HasOne(model, options = {})

BelongsTo

class BelongsTo(model, options = {})

Adds IntField <name>_id to model schema

Note

if custom foreignKey used it may be necessary to explicitly add corresponding field to avoid validation errors

HasMany

class HasMany(model, options = {})

MorphOne

class MorphOne(model, polymorphicName, options = {})
Arguments:
  • polymorphicName (String) –

Options:

columnNames: [String, String]
First is a database column for related id, second - for related type
morphValue: String, defaults to target model tablename
The string value associated with this relation.

MorphMany

class MorphMany(model, polymorphicName, options = {})
Arguments:
  • polymorphicName (String) –

Options:

columnNames: [String, String]
First is a database column for related id, second - for related type
morphValue: String, defaults to target model tablename
The string value associated with this relation.

MorphTo

class MorphTo(polymorphicName, targets, options = {})
Arguments:
  • polymorphicName (String) –
  • targets (Array) – list of target models

Options:

columnNames: [String, String]
First is a database column for related id, second - for related type

Adds IntField <name>_id or columnNames[0] to model schema

Adds StringField <name>_type of columnNames[1] to model schema