Properly handle LDL for polymorphic relations
This commit is contained in:
parent
855270d14a
commit
ad780604e1
|
@ -14,6 +14,7 @@ var util = require('util');
|
|||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var traverse = require('traverse');
|
||||
var i8n = require('inflection');
|
||||
|
||||
if (process.env.DEBUG === 'loopback') {
|
||||
// For back-compatibility
|
||||
|
@ -429,19 +430,39 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
|||
for (var rn in relations) {
|
||||
var r = relations[rn];
|
||||
assert(DataSource.relationTypes.indexOf(r.type) !== -1, "Invalid relation type: " + r.type);
|
||||
var targetModel = isModelClass(r.model) ? r.model : this.getModel(r.model, true);
|
||||
var targetModel, polymorphicName;
|
||||
|
||||
if (r.polymorphic && r.type !== 'belongsTo' && !r.model) {
|
||||
throw new Error('No model specified for polymorphic ' + r.type + ': ' + rn);
|
||||
}
|
||||
|
||||
if (r.polymorphic) {
|
||||
polymorphicName = typeof r.model === 'string' ? r.model : rn;
|
||||
if (typeof r.polymorphic === 'string') {
|
||||
polymorphicName = r.polymorphic;
|
||||
} else if (typeof r.polymorphic === 'object' && typeof r.polymorphic.as === 'string') {
|
||||
polymorphicName = r.polymorphic.as;
|
||||
}
|
||||
}
|
||||
|
||||
if (r.model) {
|
||||
targetModel = isModelClass(r.model) ? r.model : this.getModel(r.model, true);
|
||||
}
|
||||
|
||||
var throughModel = null;
|
||||
if (r.through) {
|
||||
throughModel = isModelClass(r.through) ? r.through : this.getModel(r.through, true);
|
||||
}
|
||||
if ((!r.polymorphic && !isModelDataSourceAttached(targetModel)) || (throughModel && !isModelDataSourceAttached(throughModel))) {
|
||||
|
||||
if ((targetModel && !isModelDataSourceAttached(targetModel))
|
||||
|| (throughModel && !isModelDataSourceAttached(throughModel))) {
|
||||
// Create a listener to defer the relation set up
|
||||
createListener(rn, r, targetModel, throughModel);
|
||||
} else {
|
||||
// The target model is resolved
|
||||
var params = traverse(r).clone();
|
||||
params.as = rn;
|
||||
params.model = targetModel;
|
||||
params.model = polymorphicName || targetModel;
|
||||
if (throughModel) {
|
||||
params.through = throughModel;
|
||||
}
|
||||
|
|
|
@ -115,12 +115,19 @@ function RelationDefinition(definition) {
|
|||
}
|
||||
|
||||
RelationDefinition.prototype.toJSON = function () {
|
||||
var polymorphic = typeof this.polymorphic === 'object';
|
||||
|
||||
var modelToName = this.modelTo && this.modelTo.modelName;
|
||||
if (!modelToName && polymorphic && this.type === 'belongsTo') {
|
||||
modelToName = '<polymorphic>';
|
||||
}
|
||||
|
||||
var json = {
|
||||
name: this.name,
|
||||
type: this.type,
|
||||
modelFrom: this.modelFrom.modelName,
|
||||
keyFrom: this.keyFrom,
|
||||
modelTo: this.modelTo.modelName,
|
||||
modelTo: modelToName,
|
||||
keyTo: this.keyTo,
|
||||
multiple: this.multiple
|
||||
};
|
||||
|
@ -128,6 +135,9 @@ RelationDefinition.prototype.toJSON = function () {
|
|||
json.modelThrough = this.modelThrough.modelName;
|
||||
json.keyThrough = this.keyThrough;
|
||||
}
|
||||
if (polymorphic) {
|
||||
json.polymorphic = this.polymorphic;
|
||||
}
|
||||
return json;
|
||||
};
|
||||
|
||||
|
@ -449,14 +459,20 @@ function lookupModel(models, modelName) {
|
|||
|
||||
function lookupModelTo(modelFrom, modelTo, params, singularize) {
|
||||
if ('string' === typeof modelTo) {
|
||||
var modelToName;
|
||||
params.as = params.as || modelTo;
|
||||
modelTo = params.model || modelTo;
|
||||
if (typeof modelTo === 'string') {
|
||||
var modelToName = (singularize ? i8n.singularize(modelTo) : modelTo).toLowerCase();
|
||||
modelToName = (singularize ? i8n.singularize(modelTo) : modelTo).toLowerCase();
|
||||
modelTo = lookupModel(modelFrom.dataSource.modelBuilder.models, modelToName) || modelTo;
|
||||
}
|
||||
if (typeof modelTo === 'string') {
|
||||
modelToName = (singularize ? i8n.singularize(params.as) : params.as).toLowerCase();
|
||||
console.log(modelToName)
|
||||
modelTo = lookupModel(modelFrom.dataSource.modelBuilder.models, modelToName) || modelTo;
|
||||
}
|
||||
if (typeof modelTo !== 'function') {
|
||||
throw new Error('Could not find "' + modelTo + '" relation for ' + modelFrom.modelName);
|
||||
throw new Error('Could not find "' + params.as + '" relation for ' + modelFrom.modelName);
|
||||
}
|
||||
}
|
||||
return modelTo;
|
||||
|
@ -510,7 +526,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) {
|
|||
|
||||
if (params.polymorphic) {
|
||||
polymorphic = polymorphicParams(params.polymorphic);
|
||||
polymorphic.invert = !!params.invert;
|
||||
if (params.invert) polymorphic.invert = true;
|
||||
discriminator = polymorphic.discriminator;
|
||||
if (!params.invert) {
|
||||
fk = polymorphic.foreignKey;
|
||||
|
@ -1172,7 +1188,7 @@ BelongsTo.prototype.related = function (refresh, params) {
|
|||
self.resetCache(params);
|
||||
} else if (typeof params === 'function') { // acts as async getter
|
||||
|
||||
if (discriminator && !modelTo) {
|
||||
if (discriminator) {
|
||||
var modelToName = modelInstance[discriminator];
|
||||
if (typeof modelToName !== 'string') {
|
||||
throw new Error('Polymorphic model not found: `' + discriminator + '` not set');
|
||||
|
|
|
@ -923,7 +923,51 @@ describe('Load models with relations', function () {
|
|||
assert(User.relations['posts']);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('should set up polymorphic relations', function (done) {
|
||||
var ds = new DataSource('memory');
|
||||
|
||||
var Author = ds.define('Author', {name: String}, {relations: {
|
||||
pictures: {type: 'hasMany', model: 'Picture', polymorphic: 'imageable'}
|
||||
}});
|
||||
var Picture = ds.define('Picture', {name: String}, {relations: {
|
||||
imageable: {type: 'belongsTo', polymorphic: true}
|
||||
}});
|
||||
|
||||
assert(Author.relations['pictures']);
|
||||
assert.deepEqual(Author.relations['pictures'].toJSON(), {
|
||||
name: 'pictures',
|
||||
type: 'hasMany',
|
||||
modelFrom: 'Author',
|
||||
keyFrom: 'id',
|
||||
modelTo: 'Picture',
|
||||
keyTo: 'imageableId',
|
||||
multiple: true,
|
||||
polymorphic: {
|
||||
as: 'imageable',
|
||||
foreignKey: 'imageableId',
|
||||
discriminator: 'imageableType'
|
||||
}
|
||||
});
|
||||
|
||||
assert(Picture.relations['imageable']);
|
||||
assert.deepEqual(Picture.relations['imageable'].toJSON(), {
|
||||
name: 'imageable',
|
||||
type: 'belongsTo',
|
||||
modelFrom: 'Picture',
|
||||
keyFrom: 'imageableId',
|
||||
modelTo: '<polymorphic>',
|
||||
keyTo: 'id',
|
||||
multiple: false,
|
||||
polymorphic: {
|
||||
as: 'imageable',
|
||||
foreignKey: 'imageableId',
|
||||
discriminator: 'imageableType'
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('should set up foreign key with the correct type', function (done) {
|
||||
var ds = new DataSource('memory');
|
||||
|
||||
|
|
|
@ -686,6 +686,37 @@ describe('relations', function () {
|
|||
discriminator: 'imageableType'
|
||||
} });
|
||||
Picture.belongsTo('imageable', { polymorphic: true });
|
||||
|
||||
Author.relations['pictures'].toJSON().should.eql({
|
||||
name: 'pictures',
|
||||
type: 'hasMany',
|
||||
modelFrom: 'Author',
|
||||
keyFrom: 'id',
|
||||
modelTo: 'Picture',
|
||||
keyTo: 'imageableId',
|
||||
multiple: true,
|
||||
polymorphic: {
|
||||
as: 'imageable',
|
||||
foreignKey: 'imageableId',
|
||||
discriminator: 'imageableType'
|
||||
}
|
||||
});
|
||||
|
||||
Picture.relations['imageable'].toJSON().should.eql({
|
||||
name: 'imageable',
|
||||
type: 'belongsTo',
|
||||
modelFrom: 'Picture',
|
||||
keyFrom: 'imageableId',
|
||||
modelTo: '<polymorphic>',
|
||||
keyTo: 'id',
|
||||
multiple: false,
|
||||
polymorphic: {
|
||||
as: 'imageable',
|
||||
foreignKey: 'imageableId',
|
||||
discriminator: 'imageableType'
|
||||
}
|
||||
});
|
||||
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue