Overview
LoopBack supports polymorphic relations in which a model can belong to more than one other model, on a single association.
The examples below use the following example models: Delivery
, Letter
,
Parcel
, where a Delivery
may contain a Deliverable
- either a Letter
or
a Parcel
.
Model definition
We first define Deliverable
s:
class Deliverable extends Entity {
}
class Letter extends Deliverable {
}
class Parcel extends Deliverable {
}
...
The relation between the Delivery
and Deliverable
s is: Delivery
hasOne
Deliverable
; the relation is polymorphic.
class Delivery extends Entity {
@hasOne(() => Deliverable, {polymorphic: true})
deliverable: Deliverable;
deliverableType: string;
}
...
A customized discriminator can be specified. The discriminator parameter
specifies the name of a field in the model indicating the actual type of the
Deliverable
.
class Delivery extends Entity {
@hasOne(() => Deliverable, {polymorphic: {discriminator: "deliverable_type"}})
deliverable: Deliverable;
deliverable_type: string;
}
...
Setting up repository
The only difference on the repository class is that instead of having a single repository getter, a dictionary of subclass repository getters is passed into the repository factory creator.
export class DeliveryRepository extends DefaultCrudRepository {
public readonly deliverable: HasOneRepositoryFactory<Deliverable, typeof Delivery.prototype.id>;
constructor(
dataSource: juggler.DataSource,
@repository.getter('LetterRepository')
protected letterRepositoryGetter: Getter<EntityCrudRepository<Deliverable, typeof Deliverable.prototype.id, DeliverableRelations>>,
@repository.getter('ParcelRepository')
protected parcelRepositoryGetter: Getter<EntityCrudRepository<Deliverable, typeof Deliverable.prototype.id, DeliverableRelations>>,
) {
super(Delivery, dataSource);
this.deliverable = this.createHasOneRepositoryFactoryFor(
'deliverable',
// use a dictionary of repoGetters instead of a single repoGetter instance
{Letter: letterRepositoryGetter, Parcel: parcelRepositoryGetter},
);
this.registerInclusionResolver('deliverable', this.deliverable.inclusionResolver);
}
}
...
Operations
The polymorphic relation supports queries with inclusions
const result = await deliveryRepo.findById(delivery1.id, {
include: ['deliverable'],
});
...
The instances of polymorphic relations can be created through repository operations
deliveryRepo
.deliverable(deliveryId)
.create(letter1Data, {polymorphicType: "Letter"});
...
belongsTo, hasMany, hasManyThrough relations
Polymorphic relation on belongsTo
is similar to hasOne
.
Polymorphic relation is not supported on hasMany
relations. However, there is
an easy workaround: having a hasMany
relation on a non-polymorphic concrete
type which hasOne
polymorphic relation with an abstract polymorphic type. For
example, Delivery
hasMany
DeliveryDeliverablePair
and
DeliveryDeliverablePair
hasOne
Deliverable
.
The polymorphic relation on hasManyThrough
relation is on the target model,
not the through model. The discriminator is defined on the through model, and
the target model is polymorphic.