Persist changes on parent for embedsOne

Allow direct save of changes on embedded model to be persisted on
parent document.

    Person.embedsOne(Address);
    Person.findById(someId)
      .then(function(p){
        var address = p.addressItem();
        address.street = 'new street'
        // This will now persist changes on parent document
        return address.save();
      })
This commit is contained in:
Dimitris Halatsis 2016-05-31 10:44:17 +03:00 committed by Miroslav Bajtoš
parent baef033115
commit af8cf19242
2 changed files with 99 additions and 3 deletions

View File

@ -2068,7 +2068,7 @@ EmbedsOne.prototype.related = function(condOrRefresh, options, cb) {
return;
}
var embeddedInstance = modelInstance[propertyName];
var embeddedInstance = this.embeddedValue();
if (embeddedInstance) {
embeddedInstance.__persisted = true;
@ -2083,9 +2083,56 @@ EmbedsOne.prototype.related = function(condOrRefresh, options, cb) {
}
};
EmbedsOne.prototype.prepareEmbeddedInstance = function(inst) {
if (inst && inst.triggerParent !== 'function') {
var self = this;
var propertyName = this.definition.keyFrom;
var modelInstance = this.modelInstance;
if (this.definition.options.persistent) {
var pk = this.definition.keyTo;
inst.__persisted = !!inst[pk];
} else {
inst.__persisted = true;
}
inst.triggerParent = function(actionName, callback) {
if (actionName === 'save') {
var embeddedValue = self.embeddedValue();
modelInstance.updateAttribute(propertyName,
embeddedValue, function(err, modelInst) {
callback(err, err ? null : modelInst);
});
} else if (actionName === 'destroy') {
modelInstance.unsetAttribute(propertyName, true);
// cannot delete property completely the way save works. operator $unset needed like mongo
modelInstance.save(function(err, modelInst) {
callback(err, modelInst);
});
} else {
process.nextTick(callback);
}
};
var originalTrigger = inst.trigger;
inst.trigger = function(actionName, work, data, callback) {
if (typeof work === 'function') {
var originalWork = work;
work = function(next) {
originalWork.call(this, function(done) {
inst.triggerParent(actionName, function(err, inst) {
next(done); // TODO [fabien] - error handling?
});
});
};
}
originalTrigger.call(this, actionName, work, data, callback);
};
}
};
EmbedsOne.prototype.embeddedValue = function(modelInstance) {
modelInstance = modelInstance || this.modelInstance;
return modelInstance[this.definition.keyFrom];
var embeddedValue = modelInstance[this.definition.keyFrom];
this.prepareEmbeddedInstance(embeddedValue);
return embeddedValue;
};
EmbedsOne.prototype.create = function(targetModelData, options, cb) {
@ -2186,6 +2233,8 @@ EmbedsOne.prototype.build = function(targetModelData) {
var embeddedInstance = new modelTo(targetModelData);
modelInstance[propertyName] = embeddedInstance;
this.prepareEmbeddedInstance(embeddedInstance);
return embeddedInstance;
};
@ -2203,7 +2252,7 @@ EmbedsOne.prototype.update = function(targetModelData, options, cb) {
var isInst = targetModelData instanceof ModelBaseClass;
var data = isInst ? targetModelData.toObject() : targetModelData;
var embeddedInstance = modelInstance[propertyName];
var embeddedInstance = this.embeddedValue();
if (embeddedInstance instanceof modelTo) {
cb = cb || utils.createPromiseCallback();
var hookState = {};

View File

@ -3775,6 +3775,53 @@ describe('relations', function() {
done();
}).catch(done);
});
it('should also save changes when directly saving the embedded model', function(done) {
// Passport should normally have an id for the direct save to work. For now override the check
var originalHasPK = Passport.definition.hasPK;
Passport.definition.hasPK = function() { return true; };
Person.findById(personId)
.then(function(p) {
return p.passportItem.create({ name: 'Mitsos' });
})
.then(function(passport) {
passport.name = 'Jim';
return passport.save();
})
.then(function() {
return Person.findById(personId);
})
.then(function(person) {
person.passportItem().toObject().should.eql({ name: 'Jim' });
// restore original hasPk
Passport.definition.hasPK = originalHasPK;
done();
})
.catch(function(err) {
Passport.definition.hasPK = originalHasPK;
done(err);
});
});
it('should delete the embedded document and also update parent', function(done) {
var originalHasPK = Passport.definition.hasPK;
Passport.definition.hasPK = function() { return true; };
Person.findById(personId)
.then(function(p) {
return p.passportItem().destroy();
})
.then(function() {
return Person.findById(personId);
})
.then(function(person) {
person.should.have.property('passport', null);
done();
})
.catch(function(err) {
Passport.definition.hasPK = originalHasPK;
done(err);
});
});
});
describe('embedsOne - persisted model', function() {