Polymorphic lookup from all registered dataSources
Polymorphic model lookup was previously limited to the same dataSource as the modelFrom model, which turns out to be too restrictive. This was uncovered by the use of a Transient model, not being able to lookup a PersistedModel.
This commit is contained in:
parent
07dbbd4224
commit
2c0ffee2d3
6
index.js
6
index.js
|
@ -4,6 +4,12 @@ exports.ModelBaseClass = require('./lib/model.js');
|
|||
exports.GeoPoint = require('./lib/geo.js').GeoPoint;
|
||||
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
||||
|
||||
var dataSources = exports.dataSources = {};
|
||||
|
||||
exports.registerDataSource = function(ds) {
|
||||
dataSources[ds.name] = ds;
|
||||
};
|
||||
|
||||
exports.__defineGetter__('version', function () {
|
||||
return require('./package.json').version;
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
var i8n = require('inflection');
|
||||
var jdb = require('../');
|
||||
var defineScope = require('./scope.js').defineScope;
|
||||
var mergeQuery = require('./scope.js').mergeQuery;
|
||||
var ModelBaseClass = require('./model.js');
|
||||
|
@ -466,7 +467,7 @@ function findBelongsTo(modelFrom, modelTo, keyTo) {
|
|||
* @param {String} modelName The model name
|
||||
* @returns {*} The matching model class
|
||||
*/
|
||||
function lookupModel(models, modelName) {
|
||||
function lookupModel(models, modelName, internal) {
|
||||
if(models[modelName]) {
|
||||
return models[modelName];
|
||||
}
|
||||
|
@ -476,6 +477,15 @@ function lookupModel(models, modelName) {
|
|||
return models[name];
|
||||
}
|
||||
}
|
||||
if (internal) return;
|
||||
|
||||
var keys = Object.keys(jdb.dataSources);
|
||||
for (var k = 0; k < keys.length; k++) {
|
||||
var ds = jdb.dataSources[keys[k]];
|
||||
var models = ds.modelBuilder.models;
|
||||
var model = lookupModel(models, modelName, true);
|
||||
if (model) return model;
|
||||
}
|
||||
}
|
||||
|
||||
function lookupModelTo(modelFrom, modelTo, params, singularize) {
|
||||
|
@ -503,9 +513,9 @@ function lookupModelTo(modelFrom, modelTo, params, singularize) {
|
|||
* @param {Object|String} params Name of the polymorphic relation or params
|
||||
* @returns {Object} The normalized parameters
|
||||
*/
|
||||
function polymorphicParams(params) {
|
||||
function polymorphicParams(params, as) {
|
||||
if (typeof params === 'string') params = { as: params };
|
||||
if (typeof params.as !== 'string') params.as = 'reference'; // default
|
||||
if (typeof params.as !== 'string') params.as = as || 'reference'; // default
|
||||
params.foreignKey = params.foreignKey || i8n.camelize(params.as + '_id', true);
|
||||
params.discriminator = params.discriminator || i8n.camelize(params.as + '_type', true);
|
||||
return params;
|
||||
|
@ -1079,21 +1089,23 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
|
|||
|
||||
var idName, relationName, fk;
|
||||
if (params.polymorphic) {
|
||||
relationName = params.as || (typeof modelTo === 'string' ? modelTo : null); // initially
|
||||
|
||||
if (params.polymorphic === true) {
|
||||
// modelTo arg will be the name of the polymorphic relation (string)
|
||||
polymorphic = polymorphicParams(modelTo);
|
||||
polymorphic = polymorphicParams(modelTo, relationName);
|
||||
} else {
|
||||
polymorphic = polymorphicParams(params.polymorphic);
|
||||
polymorphic = polymorphicParams(params.polymorphic, relationName);
|
||||
}
|
||||
|
||||
modelTo = null; // will lookup dynamically
|
||||
|
||||
idName = params.idName || 'id';
|
||||
relationName = params.as || polymorphic.as;
|
||||
relationName = params.as || polymorphic.as; // finally
|
||||
fk = polymorphic.foreignKey;
|
||||
discriminator = polymorphic.discriminator;
|
||||
|
||||
if (typeof polymorphic.idType === 'string') { // explicit key type
|
||||
if (polymorphic.idType) { // explicit key type
|
||||
modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: polymorphic.idType, index: true });
|
||||
} else { // try to use the same foreign key type as modelFrom
|
||||
modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName);
|
||||
|
@ -2208,10 +2220,7 @@ EmbedsMany.prototype.build = function(targetModelData) {
|
|||
|
||||
var assignId = (forceId || targetModelData[pk] === undefined);
|
||||
|
||||
if (assignId && typeof connector.generateId === 'function') {
|
||||
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
|
||||
targetModelData[pk] = id;
|
||||
} else if (assignId && pkType === Number) {
|
||||
if (assignId && pkType === Number) {
|
||||
var ids = embeddedList.map(function(m) {
|
||||
return (typeof m[pk] === 'number' ? m[pk] : 0);
|
||||
});
|
||||
|
@ -2220,6 +2229,9 @@ EmbedsMany.prototype.build = function(targetModelData) {
|
|||
} else {
|
||||
targetModelData[pk] = 1;
|
||||
}
|
||||
} else if (assignId && typeof connector.generateId === 'function') {
|
||||
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
|
||||
targetModelData[pk] = id;
|
||||
}
|
||||
|
||||
this.definition.applyProperties(modelInstance, targetModelData);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// This test written in mocha+should.js
|
||||
var should = require('./init.js');
|
||||
var Schema = require('../').Schema;
|
||||
var jdb = require('../');
|
||||
var Schema = jdb.Schema;
|
||||
|
||||
var db, tmp, Book, Chapter, Author, Reader;
|
||||
var Category, Job;
|
||||
|
@ -2420,11 +2421,20 @@ describe('relations', function () {
|
|||
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
tmp = getTransientSchema();
|
||||
|
||||
// register here, so transient models
|
||||
// can lookup related models (polymorphic)
|
||||
jdb.registerDataSource(db);
|
||||
|
||||
Book = db.define('Book', {name: String});
|
||||
Author = db.define('Author', {name: String});
|
||||
Reader = db.define('Reader', {name: String});
|
||||
|
||||
Link = db.define('Link', {name: String, notes: String}); // generic model
|
||||
Link = tmp.define('Link', {
|
||||
id: {type: Number, id: true},
|
||||
name: String, notes: String
|
||||
}); // generic model
|
||||
Link.validatesPresenceOf('linkedId');
|
||||
Link.validatesPresenceOf('linkedType');
|
||||
|
||||
|
@ -2438,13 +2448,15 @@ describe('relations', function () {
|
|||
});
|
||||
|
||||
it('can be declared', function (done) {
|
||||
var idType = db.connector.getDefaultIdType();
|
||||
|
||||
Book.embedsMany(Link, { as: 'people',
|
||||
polymorphic: 'linked',
|
||||
scope: { include: 'linked' }
|
||||
});
|
||||
Link.belongsTo('linked', {
|
||||
polymorphic: true, // needs unique auto-id
|
||||
properties: { name: 'name' }, // denormalized
|
||||
polymorphic: { idType: idType }, // native type
|
||||
properties: { name: 'name' }, // denormalized
|
||||
options: { invertProperties: true }
|
||||
});
|
||||
db.automigrate(done);
|
||||
|
|
Loading…
Reference in New Issue