Implemented polymorphic hasAndBelongsToMany

This commit is contained in:
Fabien Franzen 2014-07-26 14:54:54 +02:00
parent 9b97e1ae77
commit 295e6fc1f1
2 changed files with 129 additions and 21 deletions

View File

@ -365,13 +365,14 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) {
var fk = params.foreignKey || i8n.camelize(thisClassName + '_id', true);
var idName = modelFrom.dataSource.idName(modelFrom.modelName) || 'id';
var typeTo;
var polymorphic, typeTo;
if (typeof params.polymorphic === 'string') {
fk = i8n.camelize(params.polymorphic + '_id', true);
var typeTo = i8n.camelize(params.polymorphic + '_type', true);
polymorphic = params.polymorphic;
fk = i8n.camelize(polymorphic + '_id', true);
typeTo = i8n.camelize(polymorphic + '_type', true);
if (!params.through) {
modelTo.dataSource.defineProperty(modelTo.modelName, typeTo, { type: 'string' });
modelTo.dataSource.defineProperty(modelTo.modelName, typeTo, { type: 'string', index: true });
}
}
@ -389,12 +390,11 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) {
options: params.options
});
if (params.through) {
definition.modelThrough = params.through;
var keyThrough = definition.throughKey || i8n.camelize(modelTo.modelName + '_id', true);
definition.keyThrough = keyThrough;
}
definition.modelThrough = params.through;
var keyThrough = definition.throughKey || i8n.camelize(modelTo.modelName + '_id', true);
definition.keyThrough = keyThrough;
modelFrom.relations[relationName] = definition;
if (!params.through) {
@ -447,6 +447,7 @@ RelationDefinition.hasMany = function hasMany(modelFrom, modelTo, params) {
filter.collect = i8n.camelize(modelTo.modelName, true);
filter.include = filter.collect;
}
return filter;
}, scopeMethods);
@ -645,7 +646,7 @@ HasManyThrough.prototype.create = function create(data, done) {
var definition = this.definition;
var modelTo = definition.modelTo;
var modelThrough = definition.modelThrough;
if (typeof data === 'function' && !done) {
done = data;
data = {};
@ -660,9 +661,16 @@ HasManyThrough.prototype.create = function create(data, done) {
}
// The primary key for the target model
var pk2 = definition.modelTo.definition.idName();
var fk1 = findBelongsTo(modelThrough, definition.modelFrom,
definition.keyFrom);
var fk2 = findBelongsTo(modelThrough, definition.modelTo, pk2);
if (definition.typeTo) { // polymorphic
var fk1 = definition.keyTo;
var fk2 = definition.keyThrough;
} else {
var fk1 = findBelongsTo(modelThrough, definition.modelFrom,
definition.keyFrom);
var fk2 = findBelongsTo(modelThrough, definition.modelTo, pk2);
}
var d = {};
d[fk1] = modelInstance[definition.keyFrom];
d[fk2] = to[pk2];
@ -841,12 +849,12 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
var typeTo = i8n.camelize(polymorphic + '_type', true);
if (typeof params.idType === 'string') { // explicit key type
modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: params.idType });
modelFrom.dataSource.defineProperty(modelFrom.modelName, fk, { type: params.idType, index: true });
} else { // try to use the same foreign key type as modelFrom
modelFrom.dataSource.defineForeignKey(modelFrom.modelName, fk, modelFrom.modelName);
}
modelFrom.dataSource.defineProperty(modelFrom.modelName, typeTo, { type: 'string' });
modelFrom.dataSource.defineProperty(modelFrom.modelName, typeTo, { type: 'string', index: true });
} else {
var idName = modelFrom.dataSource.idName(modelTo.modelName) || 'id';
var relationName = params.as || i8n.camelize(modelTo.modelName, true);
@ -1055,10 +1063,22 @@ RelationDefinition.hasAndBelongsToMany = function hasAndBelongsToMany(modelFrom,
params.through = lookupModel(models, name1) || lookupModel(models, name2) ||
modelFrom.dataSource.define(name1);
}
params.through.belongsTo(modelFrom);
params.through.belongsTo(modelTo);
this.hasMany(modelFrom, modelTo, {as: params.as, through: params.through});
var options = {as: params.as, through: params.through};
if (typeof params.polymorphic === 'string') {
options.polymorphic = params.polymorphic;
var accessor = params.through.prototype[params.polymorphic];
if (typeof accessor !== 'function') { // declare once
params.through.belongsTo(modelTo);
params.through.belongsTo(params.polymorphic, { polymorphic: true });
}
} else {
params.through.belongsTo(modelFrom);
params.through.belongsTo(modelTo);
}
this.hasMany(modelFrom, modelTo, options);
};

View File

@ -3,7 +3,7 @@ var should = require('./init.js');
var db, Book, Chapter, Author, Reader;
var Category, Product;
var Picture;
var Picture, PictureLink;
describe('relations', function () {
@ -670,6 +670,94 @@ describe('relations', function () {
});
});
describe('polymorphic hasAndBelongsToMany through', function () {
before(function (done) {
db = getSchema();
Picture = db.define('Picture', {name: String});
Author = db.define('Author', {name: String});
Reader = db.define('Reader', {name: String});
PictureLink = db.define('PictureLink', {});
db.automigrate(function () {
Picture.destroyAll(function () {
PictureLink.destroyAll(function () {
Author.destroyAll(function () {
Reader.destroyAll(done);
});
});
});
});
});
it('can be declared', function (done) {
Author.hasAndBelongsToMany(Picture, { through: PictureLink, polymorphic: 'imageable' });
Reader.hasAndBelongsToMany(Picture, { through: PictureLink, polymorphic: 'imageable' });
db.automigrate(done);
});
it('should create polymorphic relation - author', function (done) {
Author.create({ id: 3, name: 'Author 1' }, function (err, author) {
author.pictures.create({ name: 'Author Pic 1' }, function (err, p) {
should.not.exist(err);
p.id.should.equal(1);
author.pictures.create({ name: 'Author Pic 2' }, function (err, p) {
should.not.exist(err);
p.id.should.equal(2);
done();
});
});
});
});
it('should create polymorphic relation - reader', function (done) {
Reader.create({ id: 4, name: 'Reader 1' }, function (err, reader) {
reader.pictures.create({ name: 'Reader Pic 1' }, function (err, p) {
should.not.exist(err);
p.id.should.equal(3);
done();
});
});
});
it('should create polymorphic through model', function (done) {
PictureLink.findOne(function(err, link) {
should.not.exist(err);
link.pictureId.should.equal(1);
link.imageableId.should.equal(3);
link.imageableType.should.equal('Author');
done();
});
});
it('should get polymorphic relation through model - author', function (done) {
Author.findById(3, function(err, author) {
should.not.exist(err);
author.name.should.equal('Author 1');
author.pictures(function(err, pics) {
should.not.exist(err);
pics.should.have.length(2);
pics[0].name.should.equal('Author Pic 1');
pics[1].name.should.equal('Author Pic 2');
done();
});
});
});
it('should get polymorphic relation through model - reader', function (done) {
Reader.findById(4, function(err, reader) {
should.not.exist(err);
reader.name.should.equal('Reader 1');
reader.pictures(function(err, pics) {
should.not.exist(err);
pics.should.have.length(1);
pics[0].name.should.equal('Reader Pic 1');
done();
});
});
});
});
describe('belongsTo', function () {
var List, Item, Fear, Mind;