diff --git a/lib/relation-definition.js b/lib/relation-definition.js index f6188a02..a36c4eac 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -1865,12 +1865,26 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params) embed: true }); - modelFrom.dataSource.defineProperty(modelFrom.modelName, propertyName, { + modelFrom.dataSource.defineProperty(modelFrom.modelName, propertyName, { type: [modelTo], default: function() { return []; } }); - if (typeof modelTo.dataSource.connector.generateId !== 'function') { - modelTo.validatesPresenceOf(idName); // unique id is required + if (typeof modelTo.dataSource.connector.generateId !== 'function' + || !modelFrom.definition.settings.idInjection) { + modelFrom.validate(propertyName, function(err) { + var self = this; + var embeddedList = this[propertyName] || []; + var hasErrors = false; + embeddedList.forEach(function(item, idx) { + if (item instanceof modelTo && item[idName] == undefined) { + hasErrors = true; + var msg = 'contains invalid item at index `' + idx + '`:'; + msg += ' `' + idName + '` is blank'; + self.errors.add(propertyName, msg, 'invalid'); + } + }); + if (hasErrors) err(false); + }); } if (!params.polymorphic) { @@ -1893,13 +1907,17 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params) var self = this; var embeddedList = this[propertyName] || []; var hasErrors = false; - embeddedList.forEach(function(item) { + embeddedList.forEach(function(item, idx) { if (item instanceof modelTo) { if (!item.isValid()) { hasErrors = true; - var id = item[idName] || '(blank)'; + var id = item[idName]; var first = Object.keys(item.errors)[0]; - var msg = 'contains invalid item: `' + id + '`'; + if (id) { + var msg = 'contains invalid item: `' + id + '`'; + } else { + var msg = 'contains invalid item at index `' + idx + '`'; + } msg += ' (`' + first + '` ' + item.errors[first] + ')'; self.errors.add(propertyName, msg, 'invalid'); } diff --git a/test/relations.test.js b/test/relations.test.js index b99f31ba..3b9f7436 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -2183,6 +2183,93 @@ describe('relations', function () { }); + describe('embedsMany - persisted model', function () { + + var address0, address1, address2; + + before(function (done) { + db = getSchema(); + Person = db.define('Person', {name: String}); + Address = db.define('Address', {street: String}); + Address.validatesPresenceOf('street'); + + db.automigrate(function () { + Person.destroyAll(done); + }); + }); + + it('can be declared', function (done) { + Person.embedsMany(Address); + db.automigrate(done); + }); + + it('should create individual items (0)', function(done) { + Address.create({ street: 'Street 0' }, function(err, inst) { + address0 = inst; + done(); + }); + }); + + it('should create individual items (1)', function(done) { + Address.create({ street: 'Street 1' }, function(err, inst) { + address1 = inst; + done(); + }); + }); + + it('should create individual items (2)', function(done) { + Address.create({ street: 'Street 2' }, function(err, inst) { + address2 = inst; + done(); + }); + }); + + it('should create embedded items on scope', function(done) { + Person.create({ name: 'Fred' }, function(err, p) { + p.addressList.create(address1.toObject(), function(err, address) { + should.not.exist(err); + address.id.should.eql(address1.id); + address1.street.should.equal('Street 1'); + p.addressList.create(address2.toObject(), function(err, address) { + should.not.exist(err); + address.id.should.eql(address2.id); + address2.street.should.equal('Street 2'); + done(); + }); + }); + }); + }); + + it('should validate embedded items on scope - id', function(done) { + Person.create({ name: 'Wilma' }, function(err, p) { + p.addressList.create({ id: null, street: 'Street 1' }, function(err, address) { + should.exist(err); + err.name.should.equal('ValidationError'); + err.details.codes.addresses.should.eql(['invalid']); + var expected = 'The `Person` instance is not valid. '; + expected += 'Details: `addresses` contains invalid item at index `0`: `id` is blank.'; + err.message.should.equal(expected); + done(); + }); + }); + }); + + it('should validate embedded items on scope - street', function(done) { + Person.create({ name: 'Wilma' }, function(err, p) { + p.addressList.create({ id: 1234 }, function(err, address) { + should.exist(err); + err.name.should.equal('ValidationError'); + err.details.codes.street.should.eql(['presence']); + var expected = 'The `Address` instance is not valid. '; + expected += 'Details: `street` can\'t be blank.'; + err.message.should.equal(expected); + done(); + }); + }); + }); + + }); + describe('embedsMany - relations, scope and properties', function () { var category, job1, job2, job3;