Skip to content

Commit c98bedf

Browse files
committed
0.4.0
* Add `select()` for sparse fields * Remove `find()` restriction for integers
1 parent 4766240 commit c98bedf

File tree

7 files changed

+238
-105
lines changed

7 files changed

+238
-105
lines changed

README.md

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ let posts = await Post
3535
.where('status', 'ACTIVE')
3636
.include('user', 'category')
3737
.append('likes')
38-
.orderBy('-created_at', 'category_id')
38+
.orderBy('-created_at', 'category_id')
3939
.get()
4040

4141
```
@@ -79,7 +79,6 @@ Let's create a new object and post it:
7979

8080
```js
8181
let post = new Post()
82-
post.title = 'Cool!'
8382

8483
// or
8584

@@ -88,6 +87,7 @@ let post = new Post({title: 'Cool!'})
8887

8988
// POST /post
9089

90+
post.title = 'Another one'
9191
post.save()
9292
```
9393

@@ -216,8 +216,7 @@ import Model from './Model'
216216
export default class User extends Model {
217217

218218
// computed properties are reactive -> user.fullname
219-
 // make sure to use "get" prefix
220-
 get fullname()
219+
get fullname()
221220
{
222221
return `${this.firstname} ${this.lastname}`
223222
}
@@ -432,33 +431,48 @@ let users = await User
432431
.orderBy('firstname')
433432
.page(1)
434433
.limit(20)
435-
.get()
434+
.$get() // sometimes you will prefer $get()
436435

437436
```
438437

439-
# Nice trick
438+
# Selecting fields
440439

441-
You can build something like scoped queries.
440+
Just want only some fields?
442441

443442
```js
444-
import Model from './Model'
443+
// GET posts?fields[posts]=title,content
445444

446-
export default class Post extends Model {
447-
448-
  // make sure this is a static method
449-
  static active()  
450-
{
451-
// here you could chain more methods from vue-query-api
452-
     return this.where('status', 'active')
453-
  }
454-
}
445+
let post = await Post
446+
.select(['title', 'content'])
447+
.get()
455448
```
456-
So, you can do this:
449+
450+
With related entities:
457451

458452
```js
459-
let activePosts = await Post
460-
.active()
461-
.get()
453+
// GET posts?include=user&fields[posts]=title,content&fields[user]=firstname,age
454+
455+
let post = await Post
456+
.select({
457+
posts: ['title', 'content'],
458+
user: ['age', 'firstname']
459+
})
460+
.include('user')
461+
.get()
462+
```
463+
464+
**TIP:** If you are using spatie/laravel-query-builder, when using related entities, you must pass extra fields:
465+
466+
```js
467+
// GET posts?include=user&fields[posts]=title,content,user_id&fields[user]=id,firstname,age
468+
469+
let post = await Post
470+
.select({
471+
posts: ['title', 'content', 'user_id'], //user_id
472+
user: ['id', 'age', 'firstname'] //id
473+
})
474+
.include('user')
475+
.get()
462476
```
463477

464478

@@ -564,7 +578,9 @@ let users = data
564578

565579
// but you can use the "fetch style request" with "$get()"
566580

567-
let users = await User.$get()
581+
let users = await User
582+
.where('status', 'ACTIVE')
583+
.$get() // <---- HERE
568584
```
569585

570586
This **WILL NOT** be converted into an array of `User` model.

src/Builder.js

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
1+
/**
2+
* Prepare attributes to be parsed
3+
*/
4+
15
import Parser from './Parser';
26

37
export default class Builder {
48

5-
constructor() {
9+
constructor(model) {
10+
this.model = model
611
this.includes = []
712
this.appends = []
813
this.sorts = []
914
this.pageValue = null
1015
this.limitValue = null
16+
this.fields = {
17+
fields: {}
18+
}
1119
this.filters = {
1220
filter: {}
1321
}
1422

1523
this.parser = new Parser(this)
1624
}
1725

26+
// query string parsed
1827
query () {
1928
return this.parser.query()
2029
}
2130

31+
/**
32+
* Query builder
33+
*/
34+
2235
include (...args) {
2336
this.includes = args
2437

@@ -31,22 +44,22 @@ export default class Builder {
3144
return this
3245
}
3346

34-
page (value) {
35-
if (!Number.isInteger(value)) {
36-
throw new Error('The VALUE must be an integer on page() method.')
47+
select (...fields) {
48+
if (fields.length === 0) {
49+
throw new Error('You must specify the fields on select() method.')
3750
}
3851

39-
this.pageValue = value
40-
41-
return this
42-
}
43-
44-
limit (value) {
45-
if (!Number.isInteger(value)) {
46-
throw new Error('The VALUE must be an integer on limit() method.')
52+
// single entity .select(['age', 'firstname'])
53+
if (fields[0].constructor === String || fields[0].constructor === Array) {
54+
this.fields.fields[this.model.resource()] = fields.join(',')
4755
}
4856

49-
this.limitValue = value
57+
// related entities .select({ posts: ['title', 'content'], user: ['age', 'firstname']} )
58+
if (fields[0].constructor === Object) {
59+
Object.entries(fields[0]).forEach(([key, value]) => {
60+
this.fields.fields[key] = value.join(',')
61+
})
62+
}
5063

5164
return this
5265
}
@@ -77,4 +90,24 @@ export default class Builder {
7790

7891
return this
7992
}
80-
}
93+
94+
page (value) {
95+
if (!Number.isInteger(value)) {
96+
throw new Error('The VALUE must be an integer on page() method.')
97+
}
98+
99+
this.pageValue = value
100+
101+
return this
102+
}
103+
104+
limit (value) {
105+
if (!Number.isInteger(value)) {
106+
throw new Error('The VALUE must be an integer on limit() method.')
107+
}
108+
109+
this.limitValue = value
110+
111+
return this
112+
}
113+
}

src/Model.js

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ export default class Model extends StaticModel {
66
constructor(...atributtes) {
77
super()
88

9-
// Keep clean object if is a manual instance
9+
// Keep clean object if it is a manual instance
1010
if (atributtes.length === 0) {
11-
this._builder = new Builder()
11+
this._builder = new Builder(this)
1212
this._fromResource = null
1313
this._customResource = null
1414
} else {
@@ -28,6 +28,10 @@ export default class Model extends StaticModel {
2828
}
2929
}
3030

31+
/**
32+
* Setup
33+
*/
34+
3135
get $http () {
3236
return Model.$http
3337
}
@@ -47,7 +51,6 @@ export default class Model extends StaticModel {
4751
}
4852

4953
hasMany (model) {
50-
5154
let instance = new model
5255
let url = `${this.baseURL()}/${this.resource()}/${this.id}/${instance.resource()}`
5356

@@ -56,12 +59,44 @@ export default class Model extends StaticModel {
5659
return instance
5760
}
5861

62+
/**
63+
* Helpers
64+
*/
65+
66+
hasId () {
67+
return this.id !== undefined && this.id !== 0 && this.id !== ''
68+
}
69+
70+
endpoint () {
71+
if (this.hasId()) {
72+
return `${this.baseURL()}/${this.resource()}/${this.id}`
73+
} else {
74+
return `${this.baseURL()}/${this.resource()}`
75+
}
76+
}
77+
78+
/**
79+
* Query
80+
*/
81+
5982
include (...args) {
6083
this._builder.include(...args)
6184

6285
return this
6386
}
6487

88+
append (...args) {
89+
this._builder.append(...args)
90+
91+
return this
92+
}
93+
94+
select (...fields) {
95+
this._builder.select(...fields)
96+
97+
return this
98+
}
99+
65100
where (field, value) {
66101
this._builder.where(field, value)
67102

@@ -74,8 +109,8 @@ export default class Model extends StaticModel {
74109
return this
75110
}
76111

77-
append (...args) {
78-
this._builder.append(...args)
112+
orderBy (...args) {
113+
this._builder.orderBy(...args)
79114

80115
return this
81116
}
@@ -92,11 +127,9 @@ export default class Model extends StaticModel {
92127
return this
93128
}
94129

95-
orderBy (...args) {
96-
this._builder.orderBy(...args)
97-
98-
return this
99-
}
130+
/**
131+
* Result
132+
*/
100133

101134
first () {
102135
return this.get().then(response => {
@@ -112,12 +145,12 @@ export default class Model extends StaticModel {
112145
})
113146
}
114147

115-
find (id) {
116-
if (!Number.isInteger(id)) {
117-
throw new Error('The ID must be an integer on find() method.')
148+
find (identifier) {
149+
if (identifier === undefined) {
150+
throw new Error('You must specify the param on find() method.')
118151
}
119152

120-
let url = `${this.baseURL()}/${this.resource()}/${id}${this._builder.query()}`
153+
let url = `${this.baseURL()}/${this.resource()}/${identifier}${this._builder.query()}`
121154

122155
return this.request({
123156
url,
@@ -133,21 +166,6 @@ export default class Model extends StaticModel {
133166
})
134167
}
135168

136-
delete () {
137-
if (!this.hasId()) {
138-
throw new Error('This model has a empty ID.')
139-
}
140-
141-
let url = `${this.baseURL()}/${this.resource()}/${this.id}`
142-
143-
return this.request({
144-
url,
145-
method: 'DELETE'
146-
}).then(response => {
147-
return response
148-
})
149-
}
150-
151169
get () {
152170
let base = this._fromResource || `${this.baseURL()}/${this.resource()}`
153171
base = this._customResource || base
@@ -194,16 +212,23 @@ export default class Model extends StaticModel {
194212
})
195213
}
196214

197-
hasId () {
198-
return this.id !== undefined && this.id !== 0 && this.id !== ''
199-
}
215+
/**
216+
* CRUD operations
217+
*/
200218

201-
endpoint () {
202-
if (this.hasId()) {
203-
return `${this.baseURL()}/${this.resource()}/${this.id}`
204-
} else {
205-
return `${this.baseURL()}/${this.resource()}`
219+
delete () {
220+
if (!this.hasId()) {
221+
throw new Error('This model has a empty ID.')
206222
}
223+
224+
let url = `${this.baseURL()}/${this.resource()}/${this.id}`
225+
226+
return this.request({
227+
url,
228+
method: 'DELETE'
229+
}).then(response => {
230+
return response
231+
})
207232
}
208233

209234
save () {

0 commit comments

Comments
 (0)