Skip to content

Commit 77c28c7

Browse files
Merge pull request #256 from robsontenorio/dev
Release v1.11.0
2 parents 448d6b7 + 03cbd69 commit 77c28c7

File tree

10 files changed

+193
-36
lines changed

10 files changed

+193
-36
lines changed

docs/content/en/api/model-options.md

+17
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,23 @@ formData() {
111111
}
112112
```
113113

114+
### `stringifyOptions`
115+
- Default: `{ encode: false, arrayFormat: 'comma' }`
116+
- Returns: `object`
117+
118+
This method can be overridden in the model to configure `qs`.
119+
120+
See [qs](https://github.com/ljharb/qs#stringifying)
121+
122+
```js
123+
stringifyOptions() {
124+
return {
125+
encode: false,
126+
arrayFormat: 'comma',
127+
}
128+
}
129+
```
130+
114131
## Model Options
115132

116133
These are model-related options.

docs/content/en/configuration.md

+53
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,52 @@ export default class Model extends BaseModel {
296296
}
297297
```
298298

299+
## Configuring Query Parameters Parser
300+
301+
See the [API reference](/api/model-options#stringifyOptions) and [qs](https://github.com/ljharb/qs#stringifying)
302+
303+
We may also need to configure the parser to match our needs. By default, it is configured to match
304+
`spatie/laravel-query-builder`, which uses `comma` array format.
305+
306+
If we want, for example, to change this behaviour to `indices`, we can configure the stringify options of `qs`
307+
by overriding the `stringifyOptions` method.
308+
309+
We can globally configure this in the [Base Model](/configuration#creating-a-base-model):
310+
311+
```js{}[~/models/Model.js]
312+
import { Model as BaseModel } from 'vue-api-query'
313+
314+
export default class Model extends BaseModel {
315+
316+
// Define a base url for a REST API
317+
baseURL() {
318+
return 'http://my-api.com'
319+
}
320+
321+
// Implement a default request method
322+
request(config) {
323+
return this.$http.request(config)
324+
}
325+
326+
// Override default query parameter names
327+
parameterNames() {
328+
const defaultParams = super.parameterNames()
329+
const customParams = {
330+
include: 'include_custom'
331+
}
332+
333+
return { ...defaultParams, ...customParams }
334+
}
335+
336+
// Configure qs
337+
stringifyOptions() {
338+
return {
339+
arrayFormat: 'indices'
340+
}
341+
}
342+
}
343+
```
344+
299345
## Configuring FormData
300346

301347
See the [API reference](/api/model-options#formdata) and [object-to-formdata](https://github.com/therealparmesh/object-to-formdata#usage)
@@ -331,6 +377,13 @@ export default class Model extends BaseModel {
331377
return { ...defaultParams, ...customParams }
332378
}
333379
380+
// Configure qs
381+
stringifyOptions() {
382+
return {
383+
arrayFormat: 'indices'
384+
}
385+
}
386+
334387
// Configure object-to-formadata
335388
formData() {
336389
return {

docs/yarn.lock

+25-20
Original file line numberDiff line numberDiff line change
@@ -2437,9 +2437,9 @@ bail@^1.0.0:
24372437
integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
24382438

24392439
balanced-match@^1.0.0:
2440-
version "1.0.0"
2441-
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
2442-
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
2440+
version "1.0.2"
2441+
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
2442+
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
24432443

24442444
base64-js@^1.0.2:
24452445
version "1.3.1"
@@ -2655,9 +2655,9 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.6.4, browserslist@^4.
26552655
node-releases "^1.1.71"
26562656

26572657
buffer-from@^1.0.0:
2658-
version "1.1.1"
2659-
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
2660-
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
2658+
version "1.1.2"
2659+
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
2660+
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
26612661

26622662
buffer-json@^2.0.0:
26632663
version "2.0.0"
@@ -3209,7 +3209,7 @@ compression@^1.7.4:
32093209
32103210
version "0.0.1"
32113211
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
3212-
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
3212+
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
32133213

32143214
concat-stream@^1.5.0:
32153215
version "1.6.2"
@@ -5645,9 +5645,9 @@ loader-runner@^2.3.1, loader-runner@^2.4.0:
56455645
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
56465646

56475647
loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
5648-
version "1.4.0"
5649-
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
5650-
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
5648+
version "1.4.2"
5649+
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
5650+
integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
56515651
dependencies:
56525652
big.js "^5.2.2"
56535653
emojis-list "^3.0.0"
@@ -6067,17 +6067,22 @@ minimalistic-crypto-utils@^1.0.1:
60676067
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
60686068

60696069
minimatch@^3.0.4:
6070-
version "3.0.4"
6071-
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
6072-
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
6070+
version "3.1.2"
6071+
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
6072+
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
60736073
dependencies:
60746074
brace-expansion "^1.1.7"
60756075

6076-
minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
6076+
minimist@^1.1.1, minimist@^1.2.5:
60776077
version "1.2.6"
60786078
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
60796079
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
60806080

6081+
minimist@^1.2.0:
6082+
version "1.2.7"
6083+
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
6084+
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
6085+
60816086
minipass-collect@^1.0.2:
60826087
version "1.0.2"
60836088
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
@@ -8393,9 +8398,9 @@ source-map-resolve@^0.5.0:
83938398
urix "^0.1.0"
83948399

83958400
source-map-support@~0.5.12:
8396-
version "0.5.19"
8397-
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
8398-
integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
8401+
version "0.5.21"
8402+
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
8403+
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
83998404
dependencies:
84008405
buffer-from "^1.0.0"
84018406
source-map "^0.6.0"
@@ -8796,9 +8801,9 @@ terser-webpack-plugin@^2.3.5:
87968801
webpack-sources "^1.4.3"
87978802

87988803
terser@^4.1.2, terser@^4.3.9, terser@^4.6.12, terser@^4.6.3:
8799-
version "4.8.0"
8800-
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
8801-
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
8804+
version "4.8.1"
8805+
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f"
8806+
integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==
88028807
dependencies:
88038808
commander "^2.20.0"
88048809
source-map "~0.6.1"

index.d.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { IStringifyOptions } from 'qs'
2+
13
type Method =
24
| 'get'
35
| 'GET'
@@ -443,6 +445,14 @@ export class Model extends StaticModel {
443445
limit: string
444446
}
445447

448+
/**
449+
* This method can be overridden in the model to configure `qs`.
450+
*
451+
* @see {@link https://robsontenorio.github.io/vue-api-query/api/model-options#stringifyOptions|API Reference}
452+
* @see {@link https://github.com/ljharb/qs#stringifying|qs}
453+
*/
454+
protected stringifyOptions(): IStringifyOptions
455+
446456
/**
447457
* Query
448458
*/

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
"semantic-release": "^19.0.3"
7070
},
7171
"dependencies": {
72-
"defu": "^5.0.0",
72+
"@types/qs": "^6.9.7",
73+
"defu": "^6.0.0",
7374
"dotprop": "^1.2.0",
7475
"dset": "^3.1.2",
7576
"object-to-formdata": "^4.1.0",

src/Model.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import defu from 'defu'
1+
import { defu } from 'defu'
22
import getProp from 'dotprop'
33
import { dset as setProp } from 'dset'
44
import { serialize } from 'object-to-formdata'
@@ -188,6 +188,12 @@ export default class Model extends StaticModel {
188188
}
189189
}
190190

191+
stringifyOptions() {
192+
return {
193+
arrayFormat: 'comma'
194+
}
195+
}
196+
191197
/**
192198
* Query
193199
*/

src/Parser.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ export default class Parser {
106106
let fields = { [this.parameterNames().fields]: this.builder.fields }
107107
this.uri +=
108108
this.prepend() +
109-
qs.stringify(fields, { encode: false, arrayFormat: 'comma' })
109+
qs.stringify(fields, {
110+
encode: false,
111+
...this.builder.model.stringifyOptions()
112+
})
110113
}
111114

112115
filters() {
@@ -117,7 +120,10 @@ export default class Parser {
117120
let filters = { [this.parameterNames().filter]: this.builder.filters }
118121
this.uri +=
119122
this.prepend() +
120-
qs.stringify(filters, { encode: false, arrayFormat: 'comma' })
123+
qs.stringify(filters, {
124+
encode: false,
125+
...this.builder.model.stringifyOptions()
126+
})
121127
}
122128

123129
sorts() {
@@ -159,7 +165,7 @@ export default class Parser {
159165
this.prepend() +
160166
qs.stringify(this.builder.payload, {
161167
encode: false,
162-
arrayFormat: 'comma'
168+
...this.builder.model.stringifyOptions()
163169
})
164170
}
165171
}

tests/builder.test.js

+31
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import MockAdapter from 'axios-mock-adapter'
44
import { Model } from '../src'
55
import ModelWithParamNames from './dummy/models/ModelWithParamNames'
66
import Post from './dummy/models/Post'
7+
import PostWithOptions from './dummy/models/PostWithOptions'
78

89
describe('Query builder', () => {
910
let errorModel = {}
@@ -56,6 +57,36 @@ describe('Query builder', () => {
5657
expect(post._builder.query()).toEqual(query)
5758
})
5859

60+
test('it can change default array format option', () => {
61+
let post = PostWithOptions.include('user').whereIn('status', [
62+
'published',
63+
'archived'
64+
])
65+
66+
expect(post._builder.query()).toEqual(
67+
'?include=user&filter[status][0]=published&filter[status][1]=archived'
68+
)
69+
70+
expect(post._builder.filters).toEqual({
71+
status: ['published', 'archived']
72+
})
73+
74+
post = PostWithOptions.include('user').whereIn(
75+
['user', 'status'],
76+
['active', 'inactive']
77+
)
78+
79+
expect(post._builder.query()).toEqual(
80+
'?include=user&filter[user][status][0]=active&filter[user][status][1]=inactive'
81+
)
82+
83+
expect(post._builder.filters).toEqual({
84+
user: {
85+
status: ['active', 'inactive']
86+
}
87+
})
88+
})
89+
5990
test('include() sets properly the builder', () => {
6091
let post = Post.include('user')
6192

tests/dummy/models/PostWithOptions.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import BaseModel from './BaseModel'
2+
import Comment from './Comment'
3+
import Tag from './Tag'
4+
import User from './User'
5+
6+
export default class PostWithOptions extends BaseModel {
7+
comments() {
8+
return this.hasMany(Comment)
9+
}
10+
11+
relations() {
12+
return {
13+
user: User,
14+
'relationships.tags': Tag
15+
}
16+
}
17+
18+
stringifyOptions() {
19+
return {
20+
arrayFormat: 'indices'
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)