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:
Fabien Franzen 2014-09-04 17:31:53 +02:00
parent 07dbbd4224
commit 2c0ffee2d3
3 changed files with 45 additions and 15 deletions

View File

@ -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;
});

View File

@ -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);

View File

@ -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);