Implemented more complex scenaro: embedsMany + relations
The test case will denormalize data into the embedded object, and re-use the actual related object id as its own id.
This commit is contained in:
parent
6ed7a0a5f2
commit
da303b72a5
37
lib/dao.js
37
lib/dao.js
|
@ -1047,6 +1047,17 @@ DataAccessObject.prototype.remove =
|
|||
}, null, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a single attribute.
|
||||
* Equivalent to `setAttributes({name: value})`
|
||||
*
|
||||
* @param {String} name Name of property
|
||||
* @param {Mixed} value Value of property
|
||||
*/
|
||||
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
|
||||
this[name] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a single attribute.
|
||||
* Equivalent to `updateAttributes({name: value}, cb)`
|
||||
|
@ -1062,7 +1073,27 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
|
|||
};
|
||||
|
||||
/**
|
||||
* Update saet of attributes.
|
||||
* Update set of attributes.
|
||||
*
|
||||
* @trigger `change` hook
|
||||
* @param {Object} data Data to update
|
||||
*/
|
||||
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
|
||||
if (typeof data !== 'object') return;
|
||||
|
||||
var Model = this.constructor;
|
||||
var inst = this;
|
||||
|
||||
// update instance's properties
|
||||
for (var key in data) {
|
||||
inst.setAttribute(key, data[key]);
|
||||
}
|
||||
|
||||
Model.emit('set', inst);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update set of attributes.
|
||||
* Performs validation before updating.
|
||||
*
|
||||
* @trigger `validation`, `save` and `update` hooks
|
||||
|
@ -1086,9 +1117,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
|
|||
}
|
||||
|
||||
// update instance's properties
|
||||
for (var key in data) {
|
||||
inst[key] = data[key];
|
||||
}
|
||||
inst.setAttributes(data);
|
||||
|
||||
inst.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
|
|
|
@ -1057,6 +1057,11 @@ BelongsTo.prototype.related = function (refresh, params) {
|
|||
modelTo = params.constructor;
|
||||
modelInstance[fk] = params[pk];
|
||||
if (discriminator) modelInstance[discriminator] = params.constructor.modelName;
|
||||
|
||||
var data = {};
|
||||
this.definition.applyProperties(params, data);
|
||||
modelInstance.setAttributes(data);
|
||||
|
||||
self.resetCache(params);
|
||||
} else if (typeof params === 'function') { // acts as async getter
|
||||
|
||||
|
@ -1525,7 +1530,9 @@ RelationDefinition.embedsMany = function hasMany(modelFrom, modelTo, params) {
|
|||
};
|
||||
|
||||
EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) {
|
||||
var name = this.definition.name;
|
||||
var modelTo = this.definition.modelTo;
|
||||
var relationName = this.definition.name;
|
||||
var modelInstance = this.modelInstance;
|
||||
var self = receiver;
|
||||
|
||||
var actualCond = {};
|
||||
|
@ -1543,14 +1550,29 @@ EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb
|
|||
throw new Error('Method can be only called with one or two arguments');
|
||||
}
|
||||
|
||||
var embeddedList = self[name] || [];
|
||||
var embeddedList = self[relationName] || [];
|
||||
|
||||
this.definition.applyScope(modelInstance, actualCond);
|
||||
|
||||
var params = mergeQuery(actualCond, scopeParams);
|
||||
|
||||
if (params.where) {
|
||||
embeddedList = embeddedList ? embeddedList.filter(applyFilter(params)) : embeddedList;
|
||||
}
|
||||
|
||||
process.nextTick(function() { cb(null, embeddedList); });
|
||||
var returnRelated = function(list) {
|
||||
if (params.include) {
|
||||
modelTo.include(list, params.include, cb);
|
||||
} else {
|
||||
process.nextTick(function() { cb(null, list); });
|
||||
}
|
||||
};
|
||||
|
||||
if (actualRefresh) {
|
||||
|
||||
}
|
||||
|
||||
returnRelated(embeddedList);
|
||||
};
|
||||
|
||||
EmbedsMany.prototype.findById = function (fkId, cb) {
|
||||
|
@ -1722,10 +1744,6 @@ EmbedsMany.prototype.build = HasOne.prototype.build = function(targetModelData)
|
|||
|
||||
var embeddedList = modelInstance[relationName] || [];
|
||||
|
||||
if (typeof targetModelData === 'function' && !cb) {
|
||||
cb = targetModelData;
|
||||
targetModelData = {};
|
||||
}
|
||||
targetModelData = targetModelData || {};
|
||||
|
||||
if (typeof targetModelData[pk] !== 'number' && autoId) {
|
||||
|
|
|
@ -1221,6 +1221,9 @@ describe('relations', function () {
|
|||
});
|
||||
|
||||
describe('embedsMany', function () {
|
||||
|
||||
var address1, address2;
|
||||
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
Person = db.define('Person', {name: String});
|
||||
|
@ -1250,7 +1253,6 @@ describe('relations', function () {
|
|||
p.addressList.build.should.be.a.function;
|
||||
});
|
||||
|
||||
var address1, address2;
|
||||
it('should create embedded items on scope', function(done) {
|
||||
Person.create({ name: 'Fred' }, function(err, p) {
|
||||
p.addressList.create({ street: 'Street 1' }, function(err, addresses) {
|
||||
|
@ -1541,4 +1543,112 @@ describe('relations', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('embedsMany - relations, scope and properties', function () {
|
||||
|
||||
var product1, product2;
|
||||
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
Category = db.define('Category', {name: String});
|
||||
Product = db.define('Product', {name: String});
|
||||
Link = db.define('Link');
|
||||
|
||||
db.automigrate(function () {
|
||||
Person.destroyAll(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can be declared', function (done) {
|
||||
Category.embedsMany(Link, {
|
||||
as: 'items', // rename
|
||||
scope: { include: 'product' } // always include
|
||||
});
|
||||
Link.belongsTo(Product, {
|
||||
foreignKey: 'id', // re-use the actual product id
|
||||
properties: { id: 'id', name: 'name' }, // denormalize, transfer id
|
||||
});
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
it('should setup related items', function(done) {
|
||||
Product.create({ name: 'Product 1' }, function(err, p) {
|
||||
product1 = p;
|
||||
Product.create({ name: 'Product 2' }, function(err, p) {
|
||||
product2 = p;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create item on scope', function(done) {
|
||||
Category.create({ name: 'Category A' }, function(err, cat) {
|
||||
var link = cat.items.build();
|
||||
link.product(product1);
|
||||
var link = cat.items.build();
|
||||
link.product(product2);
|
||||
cat.save(function(err, cat) {
|
||||
var product = cat.items.at(0);
|
||||
product.id.should.equal(product1.id);
|
||||
product.name.should.equal(product1.name);
|
||||
var product = cat.items.at(1);
|
||||
product.id.should.equal(product2.id);
|
||||
product.name.should.equal(product2.name);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should include related items on scope', function(done) {
|
||||
Category.findOne(function(err, cat) {
|
||||
cat.links.should.have.length(2);
|
||||
|
||||
// denormalized properties:
|
||||
cat.items.at(0).id.should.equal(product1.id);
|
||||
cat.items.at(0).name.should.equal(product1.name);
|
||||
cat.items.at(1).id.should.equal(product2.id);
|
||||
cat.items.at(1).name.should.equal(product2.name);
|
||||
|
||||
// lazy-loaded relations
|
||||
should.not.exist(cat.items.at(0).product());
|
||||
should.not.exist(cat.items.at(1).product());
|
||||
|
||||
cat.items(function(err, items) {
|
||||
cat.items.at(0).product().should.be.instanceof(Product);
|
||||
cat.items.at(1).product().should.be.instanceof(Product);
|
||||
cat.items.at(1).product().name.should.equal('Product 2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove embedded items by id', function(done) {
|
||||
Category.findOne(function(err, cat) {
|
||||
cat.links.should.have.length(2);
|
||||
cat.items.destroy(product1.id, function(err) {
|
||||
should.not.exist(err);
|
||||
cat.links.should.have.length(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should find items on scope', function(done) {
|
||||
Category.findOne(function(err, cat) {
|
||||
cat.links.should.have.length(1);
|
||||
cat.items.at(0).id.should.equal(product2.id);
|
||||
cat.items.at(0).name.should.equal(product2.name);
|
||||
|
||||
// lazy-loaded relations
|
||||
should.not.exist(cat.items.at(0).product());
|
||||
|
||||
cat.items(function(err, items) {
|
||||
cat.items.at(0).product().should.be.instanceof(Product);
|
||||
cat.items.at(0).product().name.should.equal('Product 2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue