Skip to content

Commit 9e821fe

Browse files
committed
Experimental support for deep create.
1 parent da34a47 commit 9e821fe

File tree

3 files changed

+157
-24
lines changed

3 files changed

+157
-24
lines changed

karma.start.js

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
beforeEach(function () {
2-
localStorage.clear();
3-
});
2+
localStorage.clear()
3+
})
44

5-
window.assert = TestRunner.assert;
5+
window.assert = TestRunner.assert
66

77
TestRunner.init({
88
features: [],
@@ -11,4 +11,72 @@ TestRunner.init({
1111
adapterConfig: {
1212
debug: false
1313
}
14-
});
14+
})
15+
16+
describe('relation functionality', function () {
17+
it('nested create', function () {
18+
var adapter = this.$$adapter
19+
var User = this.$$User
20+
return adapter.create(User, {
21+
name: 'John',
22+
profile: {
23+
24+
},
25+
posts: [
26+
{
27+
content: 'foo'
28+
}
29+
]
30+
}, { with: ['profile', 'post'] }).then(function (user) {
31+
// console.log(JSON.stringify(user, null, 2))
32+
assert.isDefined(user)
33+
assert.isDefined(user.id)
34+
assert.isDefined(user.profile)
35+
assert.isDefined(user.profile.id)
36+
assert.isDefined(user.posts)
37+
assert.equal(user.posts.length, 1)
38+
assert.isDefined(user.posts[0].id)
39+
})
40+
})
41+
it('nested create many', function () {
42+
var adapter = this.$$adapter
43+
var User = this.$$User
44+
return adapter.createMany(User, [{
45+
name: 'John',
46+
profile: {
47+
48+
},
49+
posts: [
50+
{
51+
content: 'foo'
52+
}
53+
]
54+
}, {
55+
name: 'Sally',
56+
profile: {
57+
58+
},
59+
posts: [
60+
{
61+
content: 'foo'
62+
}
63+
]
64+
}], { with: ['profile', 'post'] }).then(function (users) {
65+
// console.log(JSON.stringify(users, null, 2))
66+
assert.isDefined(users[0])
67+
assert.isDefined(users[0].id)
68+
assert.isDefined(users[0].profile)
69+
assert.isDefined(users[0].profile.id)
70+
assert.isDefined(users[0].posts)
71+
assert.equal(users[0].posts.length, 1)
72+
assert.isDefined(users[0].posts[0].id)
73+
assert.isDefined(users[1])
74+
assert.isDefined(users[1].id)
75+
assert.isDefined(users[1].profile)
76+
assert.isDefined(users[1].profile.id)
77+
assert.isDefined(users[1].posts)
78+
assert.equal(users[1].posts.length, 1)
79+
assert.isDefined(users[1].posts[0].id)
80+
})
81+
})
82+
})

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@
2121
"standard": {
2222
"parser": "babel-eslint",
2323
"globals": [
24-
"localStorage"
24+
"localStorage",
25+
"beforeEach",
26+
"describe",
27+
"assert",
28+
"TestRunner",
29+
"it",
30+
"JSData",
31+
"LocalStorageAdapter"
2532
]
2633
},
2734
"scripts": {
2835
"bundle": "webpack --config webpack.config.js --colors",
2936
"doc": "jsdoc -c conf.json src && node scripts/cleanup.js",
30-
"lint": "standard src/**/*.js",
37+
"lint": "standard src/**/*.js karma.start.js",
3138
"min": "uglifyjs dist/js-data-localstorage.js -o dist/js-data-localstorage.min.js --source-map dist/js-data-localstorage.min.map -v -m -c --screw-ie8",
3239
"version": "node scripts/version.js",
3340
"banner": "node scripts/banner.js",

src/index.js

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,79 @@ addHiddenPropsToTarget(LocalStorageAdapter.prototype, {
305305
*/
306306
beforeUpdateMany: noop,
307307

308+
_create (mapper, props, opts) {
309+
const self = this
310+
const id = get(props, mapper.idAttribute) || guid()
311+
set(props, mapper.idAttribute, id)
312+
const key = self.getIdPath(mapper, opts, id)
313+
314+
// Create the record
315+
// TODO: Create related records when the "with" option is provided
316+
self.storage.setItem(key, toJson(props))
317+
self.ensureId(id, mapper, opts)
318+
return fromJson(self.storage.getItem(key))
319+
},
320+
321+
_createWithRelations (mapper, record, opts) {
322+
const self = this
323+
const fields = {}
324+
opts.with || (opts.with = [])
325+
const idAttribute = mapper.idAttribute
326+
forEachRelation(mapper, opts, function (def, __opts) {
327+
const localField = def.localField
328+
const relationData = get(record, localField)
329+
const relatedMapper = def.getRelation()
330+
if (!relationData) {
331+
return
332+
}
333+
if (def.type === 'hasMany') {
334+
fields[localField] = relationData
335+
} else if (def.type === 'hasOne') {
336+
fields[localField] = relationData
337+
} else if (def.type === 'belongsTo') {
338+
fields[localField] = self._createWithRelations(relatedMapper, relationData, __opts)
339+
set(record, def.foreignKey, get(fields[localField], relatedMapper.idAttribute))
340+
}
341+
})
342+
const _props = {}
343+
forOwn(record, function (value, key) {
344+
if (!(key in fields)) {
345+
_props[key] = value
346+
}
347+
})
348+
349+
const id = get(_props, idAttribute) || guid()
350+
set(_props, idAttribute, id)
351+
const key = self.getIdPath(mapper, opts, id)
352+
353+
// Create the record
354+
// TODO: Create related records when the "with" option is provided
355+
self.storage.setItem(key, toJson(_props))
356+
self.ensureId(id, mapper, opts)
357+
record = fromJson(self.storage.getItem(key))
358+
359+
forEachRelation(mapper, opts, function (def, __opts) {
360+
const localField = def.localField
361+
const relationData = fields[localField]
362+
const relatedMapper = def.getRelation()
363+
if (!relationData) {
364+
return
365+
}
366+
if (def.type === 'hasMany') {
367+
fields[localField] = relationData.map(function (relatedDataItem) {
368+
set(relatedDataItem, def.foreignKey, id)
369+
return self._createWithRelations(relatedMapper, relatedDataItem, __opts)
370+
})
371+
} else if (def.type === 'hasOne') {
372+
set(relationData, def.foreignKey, id)
373+
fields[localField] = self._createWithRelations(relatedMapper, relationData, __opts)
374+
}
375+
})
376+
377+
fillIn(record, fields)
378+
return record
379+
},
380+
308381
/**
309382
* Create a new record.
310383
*
@@ -329,14 +402,7 @@ addHiddenPropsToTarget(LocalStorageAdapter.prototype, {
329402
return resolve(self[op](mapper, props, opts)).then(function (_props) {
330403
// Allow for re-assignment from lifecycle hook
331404
let record = isUndefined(_props) ? props : _props
332-
const id = get(record, mapper.idAttribute) || guid()
333-
set(record, mapper.idAttribute, id)
334-
const key = self.getIdPath(mapper, opts, id)
335-
336-
// Create the record
337-
// TODO: Create related records when the "with" option is provided
338-
self.storage.setItem(key, toJson(record))
339-
self.ensureId(id, mapper, opts)
405+
record = self._createWithRelations(mapper, record, opts)
340406

341407
// afterCreate lifecycle hook
342408
op = opts.op = 'afterCreate'
@@ -377,16 +443,8 @@ addHiddenPropsToTarget(LocalStorageAdapter.prototype, {
377443
return resolve(self[op](mapper, props, opts)).then(function (_props) {
378444
// Allow for re-assignment from lifecycle hook
379445
let records = isUndefined(_props) ? props : _props
380-
const idAttribute = mapper.idAttribute
381-
382-
// Create the record
383-
// TODO: Create related records when the "with" option is provided
384-
records.forEach(function (record) {
385-
const id = get(record, idAttribute) || guid()
386-
set(record, idAttribute, id)
387-
const key = self.getIdPath(mapper, opts, id)
388-
self.storage.setItem(key, toJson(record))
389-
self.ensureId(id, mapper, opts)
446+
records = records.map(function (record) {
447+
return self._createWithRelations(mapper, record, opts)
390448
})
391449

392450
// afterCreateMany lifecycle hook

0 commit comments

Comments
 (0)