Skip to content

Commit 49efdf5

Browse files
Merge pull request #125 from JoaoPedroAS51/feat/instance-nested-objects
feat(model): apply instances of relationships to nested objects
2 parents 0692a00 + e676162 commit 49efdf5

File tree

12 files changed

+328
-60
lines changed

12 files changed

+328
-60
lines changed

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,90 @@ await this.posts.comments().attach(payload)
517517
await this.posts.comments().sync(payload)
518518
```
519519

520+
You can also apply a model instance to a nested object by setting the key and the model in `relations` method.
521+
522+
If the backend responds with:
523+
524+
```js
525+
// response from API for /posts/1
526+
{
527+
title: 'My title'
528+
body: 'Some text here',
529+
user: {
530+
firstName: 'John',
531+
lastName: 'Doe'
532+
}
533+
}
534+
```
535+
536+
We just need to set `user` to User model:
537+
538+
539+
**/models/Post.js**
540+
```js
541+
class Post extends Model {
542+
relations () {
543+
return {
544+
// Apply User model to `user` object
545+
user: User
546+
}
547+
}
548+
}
549+
```
550+
551+
It also works for collections. So if the backend responds with:
552+
553+
```js
554+
// response from API for /comments
555+
{
556+
text: 'Some text here',
557+
user: {
558+
firstName: 'John',
559+
lastName: 'Doe'
560+
},
561+
replies: [
562+
{
563+
text: 'A reply here',
564+
user: {
565+
firstName: 'Joe',
566+
lastName: 'Doe'
567+
}
568+
},
569+
{
570+
text: 'Another reply here',
571+
user: {
572+
firstName: 'Mary',
573+
lastName: 'Doe'
574+
},
575+
replies: [
576+
{
577+
text: 'Yes, this is the reply of the reply!',
578+
user: {
579+
firstName: 'Max',
580+
lastName: 'Doe'
581+
}
582+
}
583+
]
584+
}
585+
]
586+
}
587+
```
588+
589+
Then we just need to set `user` to User model and `replies` to Comment model:
590+
591+
**/models/Comment.js**
592+
```js
593+
class Comment extends Model {
594+
relations () {
595+
return {
596+
// Apply User model to `user` object
597+
user: User,
598+
// Apply Comment model to each object of `replies` array
599+
replies: Comment
600+
}
601+
}
602+
}
603+
```
520604

521605
# Pagination
522606

src/Model.js

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default class Model extends StaticModel {
1010
this._builder = new Builder(this)
1111
} else {
1212
Object.assign(this, ...attributes)
13+
this._applyRelations(this)
1314
}
1415

1516
if (this.baseURL === undefined) {
@@ -123,6 +124,10 @@ export default class Model extends StaticModel {
123124
return this
124125
}
125126

127+
relations () {
128+
return {}
129+
}
130+
126131
/**
127132
* Helpers
128133
*/
@@ -226,6 +231,48 @@ export default class Model extends StaticModel {
226231
* Result
227232
*/
228233

234+
_applyInstance(data, model = this.constructor) {
235+
const item = new model(data)
236+
237+
if(this._fromResource) {
238+
item._from(this._fromResource)
239+
}
240+
241+
return item
242+
}
243+
244+
_applyInstanceCollection(data, model = this.constructor) {
245+
let collection = data.data || data
246+
collection = Array.isArray(collection) ? collection : [collection]
247+
248+
collection = collection.map(c => {
249+
return this._applyInstance(c, model)
250+
})
251+
return collection
252+
}
253+
254+
_applyRelations(model) {
255+
const relations = model.relations()
256+
257+
for(const relation of Object.keys(relations)) {
258+
if (!model[relation]) {
259+
return;
260+
}
261+
262+
if (Array.isArray(model[relation].data) || Array.isArray(model[relation])) {
263+
const collection = this._applyInstanceCollection(model[relation], relations[relation])
264+
265+
if (model[relation].data !== undefined) {
266+
model[relation].data = collection
267+
} else {
268+
model[relation] = collection
269+
}
270+
} else {
271+
model[relation] = this._applyInstance(model[relation], relations[relation])
272+
}
273+
}
274+
}
275+
229276
first() {
230277
return this.get().then(response => {
231278
let item
@@ -257,13 +304,7 @@ export default class Model extends StaticModel {
257304
url,
258305
method: 'GET'
259306
}).then(response => {
260-
const item = new this.constructor(response.data)
261-
262-
if (this._fromResource) {
263-
item._from(this._fromResource)
264-
}
265-
266-
return item
307+
return this._applyInstance(response.data)
267308
})
268309
}
269310

@@ -274,7 +315,7 @@ export default class Model extends StaticModel {
274315

275316
return this
276317
.find(identifier)
277-
.then(response => new this.constructor(response.data || response))
318+
.then(response => this._applyInstance(response.data || response))
278319
}
279320

280321
get() {
@@ -286,18 +327,7 @@ export default class Model extends StaticModel {
286327
url,
287328
method: 'GET'
288329
}).then(response => {
289-
let collection = response.data.data || response.data
290-
collection = Array.isArray(collection) ? collection : [collection]
291-
292-
collection = collection.map(c => {
293-
let item = new this.constructor(c)
294-
295-
if (this._fromResource) {
296-
item._from(this._fromResource)
297-
}
298-
299-
return item
300-
})
330+
let collection = this._applyInstanceCollection(response.data)
301331

302332
if (response.data.data !== undefined) {
303333
response.data.data = collection
@@ -340,8 +370,7 @@ export default class Model extends StaticModel {
340370
url: this.endpoint(),
341371
data: this
342372
}).then(response => {
343-
let self = Object.assign(this, response.data)
344-
return self
373+
return this._applyInstance(response.data)
345374
})
346375
}
347376

@@ -351,8 +380,7 @@ export default class Model extends StaticModel {
351380
url: this.endpoint(),
352381
data: this
353382
}).then(response => {
354-
let self = Object.assign(this, response.data)
355-
return self
383+
return this._applyInstance(response.data)
356384
})
357385
}
358386

@@ -375,5 +403,4 @@ export default class Model extends StaticModel {
375403
data: params
376404
}).then(response => response)
377405
}
378-
379406
}

tests/dummy/data/comments.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,34 @@ export const Comments = [
33
id: 1,
44
post_id: 1,
55
someId: 'ma018-9ha12',
6-
text: 'Hello'
6+
text: 'Hello',
7+
replies: [
8+
{
9+
id: 3,
10+
comment_id: 1,
11+
someId: 'ma020-9ha15',
12+
text: 'Hello',
13+
}
14+
]
715
},
816
{
917
id: 2,
1018
post_id: 1,
1119
someId: 'mw012-7ha19',
12-
text: 'How are you?'
20+
text: 'How are you?',
21+
replies: [
22+
{
23+
id: 4,
24+
comment_id: 2,
25+
someId: 'mw023-9ha18',
26+
text: 'Hello',
27+
},
28+
{
29+
id: 5,
30+
comment_id: 2,
31+
someId: 'mw035-0ha22',
32+
text: 'Hello',
33+
}
34+
]
1335
}
14-
]
36+
]

tests/dummy/data/commentsEmbed.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export const Comments = {
2+
data: [
3+
{
4+
id: 1,
5+
post_id: 1,
6+
someId: 'ma018-9ha12',
7+
text: 'Hello',
8+
replies: {
9+
data: [
10+
{
11+
id: 3,
12+
comment_id: 1,
13+
someId: 'ma020-9ha15',
14+
text: 'Hello',
15+
}
16+
]
17+
}
18+
},
19+
{
20+
id: 2,
21+
post_id: 1,
22+
someId: 'mw012-7ha19',
23+
text: 'How are you?',
24+
replies: {
25+
data: [
26+
{
27+
id: 4,
28+
comment_id: 2,
29+
someId: 'mw023-9ha18',
30+
text: 'Hello',
31+
},
32+
{
33+
id: 5,
34+
comment_id: 2,
35+
someId: 'mw035-0ha22',
36+
text: 'Hello',
37+
}
38+
]
39+
}
40+
}
41+
]
42+
}

tests/dummy/data/post.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ export const Post =
22
{
33
id: 1,
44
someId: 'af621-4aa41',
5-
firstname: 'John',
6-
lastname: 'Doe',
7-
age: 25
8-
}
5+
text: 'Lorem Ipsum Dolor',
6+
user: {
7+
firstname: 'John',
8+
lastname: 'Doe',
9+
age: 25
10+
}
11+
}

tests/dummy/data/postEmbed.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ export const Post = {
22
data: {
33
id: 1,
44
someId: 'af621-4aa41',
5-
firstname: 'John',
6-
lastname: 'Doe',
7-
age: 25
5+
text: 'Lorem Ipsum Dolor',
6+
user: {
7+
firstname: 'John',
8+
lastname: 'Doe',
9+
age: 25
10+
}
811
}
912
}

tests/dummy/data/posts.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@ export const Posts = [
22
{
33
id: 1,
44
someId: 'du761-8bc98',
5-
firstname: 'John',
6-
lastname: 'Doe',
7-
age: 25
5+
text: 'Lorem Ipsum Dolor',
6+
user: {
7+
firstname: 'John',
8+
lastname: 'Doe',
9+
age: 25
10+
}
811
},
912
{
1013
id: 1,
1114
someId: 'pa813-7jx02',
12-
firstname: 'Mary',
13-
lastname: 'Doe',
14-
age: 25
15+
text: 'Lorem Ipsum Dolor',
16+
user: {
17+
firstname: 'Mary',
18+
lastname: 'Doe',
19+
age: 25
20+
}
1521
}
16-
]
22+
]

0 commit comments

Comments
 (0)