---
title: "Polymorphic relations"
lang: en
layout: page
keywords: LoopBack
tags:
sidebar: lb3_sidebar
permalink: /doc/en/lb3/Polymorphic-relations.html
summary:
---

LoopBack supports _polymorphic relations_ in which a model can belong to more than one other model, on a single association.
For example, you might have a Picture model that belongs to either an Employee model or a Product model. 

The examples below use three example models: Picture, Employee, and Product, where a picture can belong to either an employee or a product.

## HasMany polymorphic relations

Take the following scenario as example:

> A Picture model belongs to either an Employee model or a Product model

A hasMany polymorphic relation means:

- There are two properties created in model Picture, one serves as a discriminator, which states what model the picture belongs to, and the other one serves as a foreignKey, which is the id(or primaryKey) value of either the employee or product.

- There are CRUD apis added to modelFrom(Employee and Product), by which you can create/modify/delete modelTo instance that belongsTo it. For example, `employee.pictures.create()` creates a new picture belongsTo employee.

If you want to have the api which retrieves the model that a modelTo instance belongsTo, which in our case is `picture.imageable()`, make sure you also define the corresponding belongsTo relation described in the next section [BelongsTo polymorphic relations](#belongsto-polymorphic-relations)

### Parameters for the definition

* `type` - the relation type, in this case is 'hasMany'
* `as` - redefines **this** relation's name (optional)
* `model` - name of modelTo
* When the `polymorphic` property is an `Object`:
  - `selector` (suggested) or as - matching **belongsTo** relation name (required) if both foreignKey and discriminator are **NOT** provided (extraneous) throws error if **BOTH** foreignKey and discriminator are provided
  - `foreignKey`:  A property of modelTo, representing the fk to modelFrom's id generated by default as `as + 'Id'`
  - `discriminator`: A property of modelTo, representing the actual modelFrom to be looked up and defined dynamically generated by default as `as + 'Type'`
* When the `polymorphic` property is a  `String` (shorthand declaration):
  - Matching **belongsTo** relation name `foreignKey` is generated as `polymorphic + 'Id'`
  - `discriminator` is generated as `polymorphic + 'Type'`

Note that `as` inside `polymorphic` object will be deprecated in LoopBack 4.  Use `selector` instead.

In the following example, model 'Employee' defines the relation with a shorthand declaration, and model 'Product' defines it with a complete polymorphic object declaration.

{% include code-caption.html content="common/models/employee.json" %}

```javascript
{
  "name": "Employee",
  "base": "PersistedModel",
  ...
  "relations": {
    "pictures": {
      "type": "hasMany",
      "model": "Picture",
      "polymorphic": "imageable"
    }
  }
...
```

And:

{% include code-caption.html content="common/models/product.json" %}
```javascript
{
  "name": "Product",
  "base": "PersistedModel",
  ...
  "relations": {
    "pictures": {
      "type": "hasMany",
      "model": "Picture",
      "polymorphic": {
        "foreignKey": "imageableId",
        "discriminator": "imageableType"
       } 
    }
  }
...
```
Alternately, provide `selector` in the `polymorphic` object:

```javascript
{
  "name": "Product",
  "base": "PersistedModel",
  ...
  "relations": {
    "pictures": {
      "type": "hasMany",
      "model": "Picture",
      "polymorphic": {
        "selector": "imageable"
       } 
    }
  }
...
```

You can also define a polymorphic hasMany relation in code:

{% include code-caption.html content="common/models/employee.js" %}
```javascript
Employee.hasMany(Picture, { polymorphic: 'imageable' });
```

And:

{% include code-caption.html content="common/models/product.js" %}
```javascript
Product.hasMany(Picture, { polymorphic: {
  foreignKey: 'imageableId',
  discriminator: 'imageableType'
  } 
});
```

Alternatively, use `selector`:

```javascript
Product.hasMany(Picture, { polymorphic: {
  selector: "imageable"
  } 
});
```

## BelongsTo polymorphic relations

Because you define the related model dynamically, you cannot declare it up front.
So, instead of passing in the related model (name), you specify the name of the polymorphic relation.

To define a belongsTo polymorphic relation, provide the following parameters:

* `type`: the relation type, in this case is 'belongsTo'
* `as`: redefines **this** relation's name (optional)
* `polymorphic`: Can be either an object or a Boolean value.
  - When its value is an object:
    * foreignKey:  A property of modelTo, representing the foreign key to modelFrom's id.
    * discriminator: A property of modelTo, representing the actual modelFrom to be looked up and defined dynamically.
  - When its value is Boolean:
    * `foreignKey` is generated as `relationName + 'Id'`.
    * `discriminator` is generated as `relationName + 'Type'`.

{% include note.html content="Do not provide a `model` field in the relation definition.  If you define it, LoopBack throws an error as relation validation.
Also, do not provide `selector` or `as` inside polymorphic object.
" %}

{% include code-caption.html content="common/models/picture.json" %}

```javascript
{
  "name": "Picture",
  "base": "PersistedModel",
  ...
  "relations": {
    "imageable": {
      "type": "belongsTo",
      "polymorphic": true
    }
  },
...
```

Alternatively, use an object for setup:

```javascript
{
  "name": "Picture",
  "base": "PersistedModel",
  ...
  "relations": {
    "imageable": {
      "type": "belongsTo",
      "polymorphic": {
        "foreignKey": "imageableId",
        "discriminator": "imageableType"
      }
    }
  },
...
```

Or, in code:

{% include code-caption.html content="common/models/picture.js" %}
```javascript
Picture.belongsTo('imageable', {
  polymorphic: true
}); 
// Alternatively, use an object for setup
Picture.belongsTo('imageable', {
  polymorphic: {
    foreignKey: 'imageableId',
    discriminator: 'imageableType'
  }
});
```

## HasOne polymorphic relations

A hasOne relation represents a "one-to-one" relation between models while a hasMany relation represents a "one-to-many" relation. For details, see [hasMany relations](HasMany-relations.html) and [hasOne relations](HasOne-relations.html).

The relation definitions in 'HasOne' is almost same as 'HasMany'; the only difference is `type` should be 'hasOne'.  For details, refer to [HasMany polymorphic relations](#hasmany-polymorphic-relations).

The following code shows how to dynamically define a 'HasOne' polymorphic relation, and also redefines the relation name with `as`: you can specify `as: 'avatar'` to explicitly set the name of the relation. If not set, it defaults to the polymorphic relation name.

{% include code-caption.html content="/common/models/model.js" %}
```javascript
Picture.belongsTo('imageable', {polymorphic: true});
Employee.hasOne(Picture, {as: 'avatar', polymorphic: 'imageable'});
Product.hasOne(Picture, {polymorphic: {as: 'imageable'}});

// To create a picture belongs to an employee, you can use the method below
employee.avatar.create();
```

## HasManyThrough polymorphic relations

To define a hasManyThrough polymorphic relation, there must be a "through" model, for example:

> Employee hasMany Picture through ImageLink polymorphically
>
> Product hasMany Picture through ImageLink polymorphically

A hasManyThrough polymorphic relation creates three properties in the the "through" model:
- The discriminator (the value is either `Employee` or `Product`)
- The foreign key (the ID or primary key value of either an employee or a product).
- A `foreignKey` property that references the Picture model.

The first two properties above are same as in the "to" model in a hasMany polymorphic relation.

Then here's an example of a polymorphic hasManyThrough relation:

First define relations in through model ImageLink.

{% include code-caption.html content="/common/models/ImageLink.json" %}
```javascript
{
  "name": "ImageLink",
  "base": "PersistedModel",
  ...
  "relations": {
    "picture": {
      "type": "belongsTo",
      "model": "Picture",
      "foreignKey": ""
      "imageable": {
        "type": "belongsTo",
        "polymorphic": true
      }
    }
  }
...
}
```

Then in the Employee (or Product) model.
{% include code-caption.html content="/common/models/Employee.json" %}
```javascript
{
  "name": "Employee",
  "base": "PersistedModel",
  ...
  "relations": {
    "pictures": {
      "type": "hasMany",
      "model": "Picture",
      "through": "ImageLink",
      "polymorphic": "imageable"
    }
  },
...
}
```

Optionally, you can define an `invert hasMany` relation in the Picture model.

{% include code-caption.html content="/common/models/Picture.json" %}
```javascript
{
  "name": "Picture",
  "base": "PersistedModel",
  ...
  "relations": {
    "employees": {
      "type": "hasMany",
      "model": "Employee",
      "through": "ImageLink",
      "invert": true,
      "polymorphic": "imageable"
    },
    "products": {
      "type": "hasMany",
      "model": "Product",
      "through": "ImageLink",
      "invert": true,
      "polymorphic": "imageable"
    }
  },
...
}
```

Equivalently, in JavaScript:

{% include code-caption.html content="/server/boot/boot-script.js" %}
```javascript
Employee.hasMany(Picture, {
  as: 'pictures',
  polymorphic: {
    foreignKey: 'imageableId',
    discriminator: 'imageableType'
  },
  through: ImageLink
});
Product.hasMany(Picture, {
  as: 'pictures',
  polymorphic: {
    foreignKey: 'imageableId',
    discriminator: 'imageableType'
  },
  through: ImageLink
});
ImageLink.belongsTo(Picture, {});
ImageLink.belongsTo(ImageLink, {polymorphic: true});

// Optionally define invert hasMany relation in Picture
Picture.hasMany(Employee, {through: ImageLink, polymorphic: 'imageable', invert: true});
Picture.hasMany(Product, {through: ImageLink, polymorphic: 'imageable', invert: true});
```

## HasAndBelongsToMany polymorphic relations

A hasAndBelongsToMany polymorphic relation is similar to a [hasManyThrough polymorphic relation](#hasmanythrough-polymorphic-relations).
It also requires an explicit 'through' model, in our example: `ImageLink`.

However, a hasAndBelongsToMany relation will automatically set up a belongsTo relation in through model, like this: `ImageLink.belongsTo(Picture, {})` and `ImageLink.belongsTo(ImageLink, {polymorphic: true})`.

{% include code-caption.html content="/common/models/model.js" %}
```javascript
Employee.hasAndBelongsToMany(Picture, {
  through: ImageLink,
  polymorphic: 'imageable'
});
Product.hasAndBelongsToMany(Picture, {
  through: ImageLink,
  polymorphic: 'imageable'
});
// Optionally, define inverse hasMany relations with '(invert: true)'
Picture.hasMany(Employee, {
  through: ImageLink,
  polymorphic: 'imageable',
  invert: true
});
Picture.hasMany(Product, {
  through: ImageLink,
  polymorphic: 'imageable',
  invert: true
});
```

## Dealing with polymorphic.idType

Because `modelTo` is unknown up-front (it's polymorphic), you cannot rely on `modelTo` for getting the `foreignKey` type.  You can explicitly declare the idType as shown below. 

The example below should provide the following results:

```javascript
[{
  url: 'john.jpg',
  imageableType: 'Employee',
  imageableId: '1',
  id: 1
}, {
  url: 'joe.jpg',
  imageableType: 'Product',
  imageableId: '1',
  id: 2
}]

Employees: [{
  username: 'John',
  id: 1
}]

Products: [{
  name: 'Joe',
  id: 1
}]
```

```javascript
var Employee = app.models.Employee;
var Product = app.models.Product;
var Picture = app.models.Picture;

Employee.hasOne(Picture, {
  as: 'avatar',
  polymorphic: {
    foreignKey: 'imageableId',
    discriminator: 'imageableType'
  }
});

Product.hasOne(Picture, {
  as: 'imageable',
  polymorphic: {
    foreignKey: 'imageableId',
    discriminator: 'imageableType'
  }
});

Picture.belongsTo('owner', {
  idName: 'username',
  polymorphic: {
    idType: Employee.definition.properties.username.type,
    foreignKey: 'imageableId',
    discriminator: 'imageableType'
  }
});

//Creating demo employee, product pictures then listing them
function createEmployee(cb) {
  Employee.create({
    username: "John"
  }).then(function(employee) {
    employee.avatar.create({
      url: "john.jpg"
    }, function() {
      cb();
    });
  });
}

function createProduct(cb) {
  Product.create({
    name: "Joe"
  }).then(function(product) {
    product.imageable.create({
      url: "joe.jpg"
    }, function() {
      cb();
    });
  });
}

function listPictures() {
  Picture.find(function(err, res) {
    console.log("\nPictures:\n", res);
  })
}

function listProducts() {
  Product.find(function(err, res) {
    console.log("\nProducts:\n", res);
  })
}

function listEmployees() {
  Employee.find(function(err, res) {
    console.log("\nEmployees:\n", res);
  })
}

//executing the demo
createEmployee(function() {
  createProduct(function() {
    listPictures();
    listEmployees();
    listProducts();
  });
});
```
