diff --git a/src/DataStore.js b/src/DataStore.js index 3266e690..44042a13 100644 --- a/src/DataStore.js +++ b/src/DataStore.js @@ -1,4 +1,5 @@ -import utils from './utils' +import utils, { safeSetLink, safeSetProp } from './utils' + import { belongsToType, hasManyType, @@ -261,22 +262,6 @@ const ownMethodsForScoping = [ 'hashQuery' ] -const safeSetProp = function (record, field, value) { - if (record && record._set) { - record._set(`props.${field}`, value) - } else { - utils.set(record, field, value) - } -} - -const safeSetLink = function (record, field, value) { - if (record && record._set) { - record._set(`links.${field}`, value) - } else { - utils.set(record, field, value) - } -} - const cachedFn = function (name, hashOrId, opts) { const cached = this._completedQueries[name][hashOrId] if (utils.isFunction(cached)) { @@ -1046,19 +1031,8 @@ const props = { // e.g. profile.user !== someUser // or comment.post !== somePost - if (currentParent) { - // e.g. otherUser.profile = undefined - if (inverseDef.type === hasOneType) { - safeSetLink(currentParent, inverseDef.localField, undefined) - } else if (inverseDef.type === hasManyType) { - // e.g. remove comment from otherPost.comments - const children = utils.get(currentParent, inverseDef.localField) - if (id === undefined) { - utils.remove(children, (child) => child === this) - } else { - utils.remove(children, (child) => child === this || id === utils.get(child, idAttribute)) - } - } + if (currentParent && inverseDef) { + this.removeInverseRelation(currentParent, id, inverseDef, idAttribute) } if (record) { // e.g. profile.user = someUser @@ -1077,18 +1051,8 @@ const props = { safeSetProp(this, foreignKey, relatedId) collection.updateIndex(this, updateOpts) - // Update (set) inverse relation - if (inverseDef.type === hasOneType) { - // e.g. someUser.profile = profile - safeSetLink(record, inverseDef.localField, this) - } else if (inverseDef.type === hasManyType) { - // e.g. add comment to somePost.comments - const children = utils.get(record, inverseDef.localField) - if (id === undefined) { - utils.noDupeAdd(children, this, (child) => child === this) - } else { - utils.noDupeAdd(children, this, (child) => child === this || id === utils.get(child, idAttribute)) - } + if (inverseDef) { + this.setupInverseRelation(record, id, inverseDef, idAttribute) } } else { // Unset in-memory link only diff --git a/src/Record.js b/src/Record.js index 37d9b811..90023262 100644 --- a/src/Record.js +++ b/src/Record.js @@ -1,6 +1,11 @@ -import utils from './utils' +import utils, { safeSetLink } from './utils' import Component from './Component' import Settable from './Settable' +import { + belongsToType, + hasManyType, + hasOneType +} from './decorators' const DOMAIN = 'Record' @@ -386,6 +391,36 @@ export default Component.extend({ return !this._mapper().validate(this, opts) }, + removeInverseRelation(currentParent, id, inverseDef, idAttribute) { + if (inverseDef.type === hasOneType) { + safeSetLink(currentParent, inverseDef.localField, undefined) + } else if (inverseDef.type === hasManyType) { + // e.g. remove comment from otherPost.comments + const children = utils.get(currentParent, inverseDef.localField) + if (id === undefined) { + utils.remove(children, (child) => child === this) + } else { + utils.remove(children, (child) => child === this || id === utils.get(child, idAttribute)) + } + } + }, + + setupInverseRelation(record, id, inverseDef, idAttribute) { + // Update (set) inverse relation + if (inverseDef.type === hasOneType) { + // e.g. someUser.profile = profile + safeSetLink(record, inverseDef.localField, this) + } else if (inverseDef.type === hasManyType) { + // e.g. add comment to somePost.comments + const children = utils.get(record, inverseDef.localField) + if (id === undefined) { + utils.noDupeAdd(children, this, (child) => child === this) + } else { + utils.noDupeAdd(children, this, (child) => child === this || id === utils.get(child, idAttribute)) + } + } + }, + /** * Lazy load relations of this record, to be attached to the record once their * loaded. diff --git a/src/utils.js b/src/utils.js index eda53641..ef09b441 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1576,6 +1576,23 @@ http://www.js-data.io/v3.0/docs/errors#${code}` } object[last] = undefined + }, + +} + +export const safeSetProp = function (record, field, value) { + if (record && record._set) { + record._set(`props.${field}`, value) + } else { + utils.set(record, field, value) + } +} + +export const safeSetLink = function (record, field, value) { + if (record && record._set) { + record._set(`links.${field}`, value) + } else { + utils.set(record, field, value) } } diff --git a/test/unit/decorators/belongsTo.test.js b/test/unit/decorators/belongsTo.test.js index 64413b7e..ee2f0b3d 100644 --- a/test/unit/decorators/belongsTo.test.js +++ b/test/unit/decorators/belongsTo.test.js @@ -77,6 +77,26 @@ describe('JSData.belongsTo', function () { assert.strictEqual(bar2.foo, foo) assert.strictEqual(bar3.foo, foo) }) + + it ('should not create an inverseLink if no inverseRelationship is defined', function() { + const store = new JSData.DataStore() + store.defineMapper('foo', { + }) + store.defineMapper('bar', { + relations: { + belongsTo: { + foo: { + localField: '_foo', + foreignKey: 'foo_id' + } + } + } + }) + const foo = store.add('foo', { id: 1 }) + const bar = store.add('bar', { id: 1, foo_id: 1 }) + assert.strictEqual(bar._foo, foo) + }) + it('should add property accessors to prototype of target and allow relation re-assignment using customizations', function () { const store = new JSData.DataStore() store.defineMapper('foo', {