Add support for hasMany-through and more tests
This commit is contained in:
parent
4fbec288c4
commit
68cf633795
|
@ -12,6 +12,7 @@ var EventEmitter = require('events').EventEmitter;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var assert = require('assert');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
var existsSync = fs.existsSync || path.existsSync;
|
var existsSync = fs.existsSync || path.existsSync;
|
||||||
|
@ -302,6 +303,13 @@ DataSource.prototype.setup = function(name, settings) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isModelClass(cls) {
|
||||||
|
if(!cls) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return cls.prototype instanceof ModelBaseClass;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a model class
|
* Define a model class
|
||||||
*
|
*
|
||||||
|
@ -383,33 +391,62 @@ DataSource.prototype.createModel = DataSource.prototype.define = function define
|
||||||
var relations = settings.relationships || settings.relations;
|
var relations = settings.relationships || settings.relations;
|
||||||
|
|
||||||
// Create a function for the closure in the loop
|
// Create a function for the closure in the loop
|
||||||
var createListener = function(name, relation, targetModel) {
|
var createListener = function (name, relation, targetModel, throughModel) {
|
||||||
targetModel.once('defined', function(model) {
|
if (targetModel && targetModel.settings.unresolved) {
|
||||||
|
targetModel.once('defined', function (model) {
|
||||||
|
// Check if the through model doesn't exist or resolved
|
||||||
|
if (!throughModel || !throughModel.settings.unresolved) {
|
||||||
// The target model is resolved
|
// The target model is resolved
|
||||||
var params = {
|
var params = {
|
||||||
foreignKey: relation.foreignKey,
|
foreignKey: relation.foreignKey,
|
||||||
as: name,
|
as: name,
|
||||||
model: model
|
model: model
|
||||||
};
|
};
|
||||||
|
if (throughModel) {
|
||||||
|
params.through = throughModel;
|
||||||
|
}
|
||||||
NewClass[relation.type].call(NewClass, name, params);
|
NewClass[relation.type].call(NewClass, name, params);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
if (throughModel && throughModel.settings.unresolved) {
|
||||||
|
// Set up a listener to the through model
|
||||||
|
throughModel.once('defined', function (model) {
|
||||||
|
if (!targetModel.settings.unresolved) {
|
||||||
|
// The target model is resolved
|
||||||
|
var params = {
|
||||||
|
foreignKey: relation.foreignKey,
|
||||||
|
as: name,
|
||||||
|
model: targetModel,
|
||||||
|
through: model
|
||||||
|
};
|
||||||
|
NewClass[relation.type].call(NewClass, name, params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the relations
|
// Set up the relations
|
||||||
if (relations) {
|
if (relations) {
|
||||||
for (var rn in relations) {
|
for (var rn in relations) {
|
||||||
var r = relations[rn];
|
var r = relations[rn];
|
||||||
if (!r.type) {
|
assert(['belongsTo', 'hasMany', 'hasAndBelongsToMany'].indexOf(r.type) !== -1, "Invalid relation type: " + r.type);
|
||||||
throw new Error('Relation type is required for ' + r);
|
var targetModel = isModelClass(r.model) ? r.model : this.models[r.model];
|
||||||
}
|
|
||||||
var targetModel = this.models[r.model];
|
|
||||||
if(!targetModel) {
|
if(!targetModel) {
|
||||||
// The target model doesn't exist, let create a place holder for it
|
// The target model doesn't exist, let create a place holder for it
|
||||||
targetModel = this.define(r.model, {}, {unresolved: true});
|
targetModel = this.define(r.model, {}, {unresolved: true});
|
||||||
}
|
}
|
||||||
if(targetModel.settings.unresolved) {
|
var throughModel = null;
|
||||||
|
if(r.through) {
|
||||||
|
throughModel = isModelClass(r.through) ? r.through : this.models[r.through];
|
||||||
|
if(!throughModel) {
|
||||||
|
// The through model doesn't exist, let create a place holder for it
|
||||||
|
throughModel = this.define(r.through, {}, {unresolved: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(targetModel.settings.unresolved || (throughModel && throughModel.settings.unresolved)) {
|
||||||
// Create a listener to defer the relation set up
|
// Create a listener to defer the relation set up
|
||||||
createListener(rn, r, targetModel);
|
createListener(rn, r, targetModel, throughModel);
|
||||||
} else {
|
} else {
|
||||||
// The target model is resolved
|
// The target model is resolved
|
||||||
var params = {
|
var params = {
|
||||||
|
@ -417,6 +454,9 @@ DataSource.prototype.createModel = DataSource.prototype.define = function define
|
||||||
as: rn,
|
as: rn,
|
||||||
model: targetModel
|
model: targetModel
|
||||||
};
|
};
|
||||||
|
if(throughModel) {
|
||||||
|
params.through = throughModel;
|
||||||
|
}
|
||||||
NewClass[r.type].call(NewClass, rn, params);
|
NewClass[r.type].call(NewClass, rn, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -444,6 +444,10 @@ describe('Load models with relations', function () {
|
||||||
var ds = new DataSource('memory');
|
var ds = new DataSource('memory');
|
||||||
|
|
||||||
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasMany', model: 'Post'}, accounts: {type: 'hasMany', model: 'Account'}}});
|
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasMany', model: 'Post'}, accounts: {type: 'hasMany', model: 'Account'}}});
|
||||||
|
|
||||||
|
assert(!User.relations['posts']);
|
||||||
|
assert(!User.relations['accounts']);
|
||||||
|
|
||||||
var Post = ds.define('Post', {userId: Number, content: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
var Post = ds.define('Post', {userId: Number, content: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
||||||
|
|
||||||
var Account = ds.define('Account', {userId: Number, type: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
var Account = ds.define('Account', {userId: Number, type: String}, {relations: {user: {type: 'belongsTo', model: 'User'}}});
|
||||||
|
@ -489,6 +493,44 @@ describe('Load models with relations', function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw if the relation type is invalid', function (done) {
|
||||||
|
var ds = new DataSource('memory');
|
||||||
|
|
||||||
|
var Post = ds.define('Post', {userId: Number, content: String});
|
||||||
|
|
||||||
|
try {
|
||||||
|
var User = ds.define('User', {name: String}, {relations: {posts: {type: 'hasXYZ', model: 'Post'}}});
|
||||||
|
} catch (e) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle hasMany through', function (done) {
|
||||||
|
var ds = new DataSource('memory');
|
||||||
|
var Physician = ds.createModel('Physician', {
|
||||||
|
name: String
|
||||||
|
}, {relations: {patients: {model: 'Patient', type: 'hasMany', through: 'Appointment'}}});
|
||||||
|
|
||||||
|
var Patient = ds.createModel('Patient', {
|
||||||
|
name: String
|
||||||
|
}, {relations: {physicians: {model: 'Physician', type: 'hasMany', through: 'Appointment'}}});
|
||||||
|
|
||||||
|
assert(!Physician.relations['patients']); // Appointment hasn't been resolved yet
|
||||||
|
assert(!Patient.relations['physicians']); // Appointment hasn't been resolved yet
|
||||||
|
|
||||||
|
var Appointment = ds.createModel('Appointment', {
|
||||||
|
physicianId: Number,
|
||||||
|
patientId: Number,
|
||||||
|
appointmentDate: Date
|
||||||
|
}, {relations: {patient: {type: 'belongsTo', model: 'Patient'}, physician: {type: 'belongsTo', model: 'Physician'}}});
|
||||||
|
|
||||||
|
assert(Physician.relations['patients']);
|
||||||
|
assert(Patient.relations['physicians']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Load models from json', function () {
|
describe('Load models from json', function () {
|
||||||
|
|
Loading…
Reference in New Issue