@codenameryuu/adonis-lucid-filter 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -134
- package/build/commands/commands.json +1 -0
- package/build/commands/main.d.ts +4 -0
- package/build/commands/main.js +36 -0
- package/build/make/filter/main.stub +20 -0
- package/package.json +14 -7
package/README.md
CHANGED
|
@@ -2,78 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
This addon adds the functionality to filter Lucid Models Adonis JS 7. Inspired by [EloquentFilter](https://github.com/Tucker-Eric/EloquentFilter)
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Requirement
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
`/users?name=Tony&lastName=&companyId=2&industry=5`
|
|
10
|
-
|
|
11
|
-
`request.all()` or `request.qs()` will return:
|
|
12
|
-
|
|
13
|
-
```json
|
|
14
|
-
{
|
|
15
|
-
"name": "Tony",
|
|
16
|
-
"lastName": "",
|
|
17
|
-
"companyId": 2,
|
|
18
|
-
"industry": 5
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
To filter by all those parameters we would need to do something like:
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
import type { HttpContext } from '@adonisjs/core/http'
|
|
26
|
-
import User from '#models/user'
|
|
27
|
-
|
|
28
|
-
export default class UsersController {
|
|
29
|
-
async index({ request }: HttpContext): Promise<User[]> {
|
|
30
|
-
const { companyId, lastName, name, industry } = request.qs()
|
|
31
|
-
|
|
32
|
-
const query = User.query().where('company_id', +companyId)
|
|
33
|
-
|
|
34
|
-
if (lastName) {
|
|
35
|
-
query.where('last_name', 'LIKE', `%${lastName}%`)
|
|
36
|
-
}
|
|
37
|
-
if (name) {
|
|
38
|
-
query.where(function () {
|
|
39
|
-
this.where('first_name', 'LIKE', `%${name}%`).orWhere('last_name', 'LIKE', `%${name}%`)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
return query.exec()
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
To filter that same input with Lucid Filters:
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
import type { HttpContext } from '@adonisjs/core/http'
|
|
51
|
-
import User from '#models/user'
|
|
52
|
-
|
|
53
|
-
export default class UsersController {
|
|
54
|
-
async index({ request }: HttpContext): Promise<User[]> {
|
|
55
|
-
return User.filter(request.qs()).exec()
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
```
|
|
7
|
+
* Adonis Js 7
|
|
8
|
+
* Lucid 22 or higher
|
|
59
9
|
|
|
60
10
|
## Installation
|
|
61
11
|
|
|
62
|
-
|
|
12
|
+
* Install the package
|
|
63
13
|
|
|
64
14
|
```bash
|
|
65
15
|
yarn add @codenameryuu/adonis-lucid-filter
|
|
66
16
|
```
|
|
67
17
|
|
|
68
|
-
|
|
18
|
+
* Configure the package
|
|
69
19
|
|
|
70
20
|
```bash
|
|
71
21
|
node ace configure @codenameryuu/adonis-lucid-filter
|
|
72
22
|
```
|
|
73
23
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- Register the provider and commands inside `adonisrc.ts` file.
|
|
24
|
+
* Make sure to register the provider inside `adonisrc.ts` file.
|
|
77
25
|
|
|
78
26
|
```typescript
|
|
79
27
|
providers: [
|
|
@@ -86,65 +34,71 @@ commands: [
|
|
|
86
34
|
]
|
|
87
35
|
```
|
|
88
36
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
> Only available if you have added `@codenameryuu/adonis-lucid-filter/commands` in `commands` array in your `adonisrc.ts'
|
|
37
|
+
## Usage
|
|
92
38
|
|
|
93
39
|
You can create a model filter with the following ace command:
|
|
94
40
|
|
|
95
41
|
```bash
|
|
96
|
-
node ace make:filter
|
|
42
|
+
node ace make:filter product
|
|
97
43
|
```
|
|
98
44
|
|
|
99
|
-
Where `
|
|
45
|
+
Where `product` is the Lucid Model you are creating the filter for. This will create `app/models/filters/product_filter.ts`
|
|
100
46
|
|
|
101
47
|
### Defining The Filter Logic
|
|
102
48
|
|
|
103
49
|
Define the filter logic based on the camel cased input key passed to the `filter()` method.
|
|
104
50
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
51
|
+
* Empty strings are ignored
|
|
52
|
+
* `setup()` will be called regardless of input
|
|
53
|
+
* `_id` is dropped from the end of the input to define the method so filtering `product_id` would use the `product()` method
|
|
54
|
+
* Input without a corresponding filter method are ignored
|
|
55
|
+
* The value of the key is injected into the method
|
|
56
|
+
* All values are accessible through the `this.$input` a property
|
|
57
|
+
* All QueryBuilder methods are accessible in `this.$query` object in the model filter class.
|
|
112
58
|
|
|
113
59
|
To define methods for the following input:
|
|
114
60
|
|
|
115
61
|
```json
|
|
116
62
|
{
|
|
117
|
-
"
|
|
118
|
-
"name": "
|
|
119
|
-
"
|
|
63
|
+
"productCategoryId": 1,
|
|
64
|
+
"name": "Car",
|
|
65
|
+
"price": 100000
|
|
120
66
|
}
|
|
121
67
|
```
|
|
122
68
|
|
|
123
69
|
You would use the following methods:
|
|
124
70
|
|
|
125
71
|
```typescript
|
|
126
|
-
import { BaseModelFilter } from 'adonis-lucid-filter'
|
|
72
|
+
import { BaseModelFilter } from '@codenameryuu/adonis-lucid-filter'
|
|
127
73
|
import type { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'
|
|
128
|
-
import
|
|
74
|
+
import Product from '#models/product'
|
|
129
75
|
|
|
130
|
-
export default class
|
|
131
|
-
declare $query: ModelQueryBuilderContract<typeof
|
|
76
|
+
export default class ProductFilter extends BaseModelFilter {
|
|
77
|
+
declare $query: ModelQueryBuilderContract<typeof Product>
|
|
132
78
|
|
|
79
|
+
// Blacklisted methods
|
|
133
80
|
static blacklist: string[] = ['secretMethod']
|
|
134
81
|
|
|
135
|
-
//
|
|
136
|
-
company(
|
|
137
|
-
|
|
82
|
+
// Dropped `_id` from the end of the input
|
|
83
|
+
// Doing this would allow you to have a `company()` filter method as well as a `companyId()` filter method.
|
|
84
|
+
static dropId: boolean = true
|
|
85
|
+
|
|
86
|
+
// Doing this would allow you to have a mobile_phone() filter method instead of mobilePhone().
|
|
87
|
+
// By default, mobilePhone() filter method can be called thanks to one of the following input key:
|
|
88
|
+
// mobile_phone, mobilePhone, mobile_phone_id, mobilePhoneId
|
|
89
|
+
static camelCase: boolean = true
|
|
90
|
+
|
|
91
|
+
// This will filter 'productCategoryId', 'product_category_id' OR 'productCategory'
|
|
92
|
+
productCategory(id: number) {
|
|
93
|
+
this.$query.where('product_category_id', id)
|
|
138
94
|
}
|
|
139
95
|
|
|
140
96
|
name(name: string) {
|
|
141
|
-
this.$query.where(
|
|
142
|
-
builder.where('first_name', 'LIKE', `%${name}%`).orWhere('last_name', 'LIKE', `%${name}%`)
|
|
143
|
-
})
|
|
97
|
+
this.$query.where('name', 'LIKE', `%${name}%`)
|
|
144
98
|
}
|
|
145
99
|
|
|
146
|
-
|
|
147
|
-
this.$query.where('
|
|
100
|
+
price(price: number) {
|
|
101
|
+
this.$query.where('price', price)
|
|
148
102
|
}
|
|
149
103
|
|
|
150
104
|
secretMethod(secretParameter: any) {
|
|
@@ -176,33 +130,16 @@ setup($query) {
|
|
|
176
130
|
In the example above `secretMethod()` will not be called, even if there is a `secret_method` key in the input object.
|
|
177
131
|
In order to call this method it would need to be whitelisted dynamically:
|
|
178
132
|
|
|
179
|
-
#### Static properties
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
export default class UserFilter extends BaseModelFilter {
|
|
183
|
-
// Blacklisted methods
|
|
184
|
-
static blacklist: string[] = []
|
|
185
|
-
|
|
186
|
-
// Dropped `_id` from the end of the input
|
|
187
|
-
// Doing this would allow you to have a `company()` filter method as well as a `companyId()` filter method.
|
|
188
|
-
static dropId: boolean = true
|
|
189
|
-
|
|
190
|
-
// Doing this would allow you to have a mobile_phone() filter method instead of mobilePhone().
|
|
191
|
-
// By default, mobilePhone() filter method can be called thanks to one of the following input key:
|
|
192
|
-
// mobile_phone, mobilePhone, mobile_phone_id, mobilePhoneId
|
|
193
|
-
static camelCase: boolean = true
|
|
194
|
-
}
|
|
195
|
-
```
|
|
196
|
-
|
|
197
133
|
### Applying The Filter To A Model
|
|
198
134
|
|
|
199
135
|
```typescript
|
|
200
|
-
import UserFilter from '#models/filters/user_filter'
|
|
201
136
|
import { compose } from '@adonisjs/core/helpers'
|
|
202
|
-
import { Filterable } from 'adonis-lucid-filter'
|
|
137
|
+
import { Filterable } from '@codenameryuu/adonis-lucid-filter'
|
|
203
138
|
|
|
204
|
-
|
|
205
|
-
|
|
139
|
+
import ProductFilter from '#models/filters/product_filter'
|
|
140
|
+
|
|
141
|
+
export default class Product extends compose(BaseModel, Filterable) {
|
|
142
|
+
static $filter = () => ProductFilter
|
|
206
143
|
|
|
207
144
|
// ...columns and props
|
|
208
145
|
}
|
|
@@ -212,18 +149,17 @@ This gives you access to the `filter()` method that accepts an object of input:
|
|
|
212
149
|
|
|
213
150
|
```typescript
|
|
214
151
|
import type { HttpContext } from '@adonisjs/core/http'
|
|
215
|
-
import
|
|
152
|
+
import Product from '#models/product'
|
|
216
153
|
|
|
217
|
-
export default class
|
|
218
|
-
async index({ request }: HttpContext): Promise<
|
|
219
|
-
return
|
|
154
|
+
export default class ProductsController {
|
|
155
|
+
async index({ request }: HttpContext): Promise<Product[]> {
|
|
156
|
+
return Product.filter(request.all()).orderBy('created_at', 'desc')
|
|
220
157
|
}
|
|
221
158
|
|
|
222
159
|
// or with paginate method
|
|
223
160
|
|
|
224
|
-
async index({ request }: HttpContext): Promise<ModelPaginatorContract<
|
|
225
|
-
|
|
226
|
-
return User.filter(input).paginate(page, 15)
|
|
161
|
+
async index({ request }: HttpContext): Promise<ModelPaginatorContract<Product>> {
|
|
162
|
+
return Product.filter(request.all()).paginate(1, 10)
|
|
227
163
|
}
|
|
228
164
|
}
|
|
229
165
|
```
|
|
@@ -235,13 +171,13 @@ Defining a filter dynamically will take precedent over any other filters defined
|
|
|
235
171
|
|
|
236
172
|
```typescript
|
|
237
173
|
import type { HttpContext } from '@adonisjs/core/http'
|
|
238
|
-
import
|
|
239
|
-
import
|
|
174
|
+
import ProductFilter from '#models/filters/product_filter'
|
|
175
|
+
import ProductExclusiveFilter from '#models/filters/product_exclusive_filter'
|
|
240
176
|
|
|
241
|
-
export default class
|
|
242
|
-
async index({ request, auth }: HttpContext): Promise<
|
|
243
|
-
const filter = auth.user.isAdmin() ?
|
|
244
|
-
return
|
|
177
|
+
export default class ProductsController {
|
|
178
|
+
async index({ request, auth }: HttpContext): Promise<Product[]> {
|
|
179
|
+
const filter = auth.user.isAdmin() ? ProductFilter : ProductExclusiveFilter
|
|
180
|
+
return Product.filter(request.all(), filter).orderBy('created_at', 'desc')
|
|
245
181
|
}
|
|
246
182
|
}
|
|
247
183
|
```
|
|
@@ -252,25 +188,23 @@ For filtering relations of model may be use `.query().filter()` or scope `filtra
|
|
|
252
188
|
|
|
253
189
|
```typescript
|
|
254
190
|
import type { HttpContext } from '@adonisjs/core/http'
|
|
255
|
-
import
|
|
191
|
+
import ProductCategory from '#models/product_category'
|
|
256
192
|
|
|
257
|
-
export default class
|
|
193
|
+
export default class ProductCategoriesController {
|
|
258
194
|
/**
|
|
259
|
-
* Get a list
|
|
260
|
-
* GET /
|
|
195
|
+
* Get a list products of product category
|
|
196
|
+
* GET /product-categories/:product_category_id/products
|
|
261
197
|
*/
|
|
262
|
-
async index({ params, request }: HttpContext): Promise<
|
|
263
|
-
const
|
|
198
|
+
async index({ params, request }: HttpContext): Promise<Product[]> {
|
|
199
|
+
const productCategory: ProductCategory = await ProductCategory.findOrFail(
|
|
200
|
+
params.product_category_id
|
|
201
|
+
)
|
|
264
202
|
|
|
265
|
-
return
|
|
266
|
-
.related('
|
|
203
|
+
return productCategory
|
|
204
|
+
.related('products')
|
|
267
205
|
.query()
|
|
268
|
-
.
|
|
269
|
-
.
|
|
270
|
-
|
|
271
|
-
// or
|
|
272
|
-
|
|
273
|
-
return user.related('posts').query().filter(request.qs()).exec()
|
|
206
|
+
.filter(request.all())
|
|
207
|
+
.orderBy('created_at', 'desc')
|
|
274
208
|
}
|
|
275
209
|
}
|
|
276
210
|
```
|
|
@@ -278,3 +212,7 @@ export default class UserPostsController {
|
|
|
278
212
|
Documentation by [Query Scopes](https://lucid.adonisjs.com/docs/model-query-scopes)
|
|
279
213
|
|
|
280
214
|
**Note:** The relation model must be `Filterable` and `$filter` must be defined in it
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
This project is [MIT](https://github.com/codenameryuu/adonis-lucid-filter/blob/master/LICENSE.md) licensed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"commands":[{"commandName":"make:filter","description":"Make a new Lucid filter","help":"","namespace":"make","aliases":[],"flags":[],"args":[{"name":"name","argumentName":"name","required":true,"description":"Name of the model file","type":"string"}],"options":{},"filePath":"make_filter.js"}],"version":1}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* In-memory cache of commands after they have been loaded
|
|
5
|
+
*/
|
|
6
|
+
let commandsMetaData
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reads the commands from the "./commands.json" file. Since, the commands.json
|
|
10
|
+
* file is generated automatically, we do not have to validate its contents
|
|
11
|
+
*/
|
|
12
|
+
export async function getMetaData() {
|
|
13
|
+
if (commandsMetaData) {
|
|
14
|
+
return commandsMetaData
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const commandsIndex = await readFile(new URL('./commands.json', import.meta.url), 'utf-8')
|
|
18
|
+
commandsMetaData = JSON.parse(commandsIndex).commands
|
|
19
|
+
|
|
20
|
+
return commandsMetaData
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Imports the command by lookingup its path from the commands
|
|
25
|
+
* metadata
|
|
26
|
+
*/
|
|
27
|
+
export async function getCommand(metaData) {
|
|
28
|
+
const commands = await getMetaData()
|
|
29
|
+
const command = commands.find(({ commandName }) => metaData.commandName === commandName)
|
|
30
|
+
if (!command) {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { default: commandConstructor } = await import(new URL(command.filePath, import.meta.url).href)
|
|
35
|
+
return commandConstructor
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{{#var entity = generators.createEntity(name)}}
|
|
2
|
+
{{#var modelName = generators.modelName(entity.name)}}
|
|
3
|
+
{{#var modelFileName = string(modelName).removeExtension().snakeCase().toString()}}
|
|
4
|
+
{{#var modelFilterName = string(modelName).removeSuffix('filter').pascalCase().suffix(string.pascalCase('filter')).toString()}}
|
|
5
|
+
{{#var modelFilterFileName = string(modelName).snakeCase().suffix('_filter').ext('.ts').toString()}}
|
|
6
|
+
{{{
|
|
7
|
+
exports({ to: app.modelsPath('filters', entity.path, modelFilterFileName) })
|
|
8
|
+
}}}
|
|
9
|
+
|
|
10
|
+
import { BaseModelFilter } from 'adonis-lucid-filter'
|
|
11
|
+
import type { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'
|
|
12
|
+
import {{ modelName }} from '#models/{{ modelFileName }}'
|
|
13
|
+
|
|
14
|
+
export default class {{ modelFilterName }} extends BaseModelFilter {
|
|
15
|
+
declare $query: ModelQueryBuilderContract<typeof {{ modelName }}>
|
|
16
|
+
|
|
17
|
+
name(value: string): void {
|
|
18
|
+
this.$query.where('name', value)
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codenameryuu/adonis-lucid-filter",
|
|
3
3
|
"description": "Addon for filtering Adonis JS 7 Lucid ORM",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
"author": "codenameryuu",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/codenameryuu/adonis-lucid-filter#readme",
|
|
@@ -26,15 +26,22 @@
|
|
|
26
26
|
"./provider": "./build/providers/lucid_filter_provider.js"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
|
+
"lint": "eslint .",
|
|
30
|
+
"format": "prettier --write .",
|
|
29
31
|
"clean": "del-cli build",
|
|
32
|
+
"precompile": "npm run lint && npm run clean",
|
|
33
|
+
"postcompile": "npm run copy:stubs && npm run index:commands",
|
|
34
|
+
"compile": "tsc",
|
|
35
|
+
"build": "npm run compile",
|
|
36
|
+
"copy:stubs": "copyfiles \"stubs/**/**/*.stub\" --up=\"1\" build",
|
|
37
|
+
"index:commands": "adonis-kit index build/commands",
|
|
38
|
+
"quick:test": "NODE_DEBUG=\"adonis-lucid-filter\" node --enable-source-maps --loader=ts-node/esm bin/test.ts",
|
|
39
|
+
"pretest": "npm run lint",
|
|
40
|
+
"test": "c8 npm run quick:test",
|
|
30
41
|
"typecheck": "tsc --noEmit",
|
|
31
|
-
"lint": "eslint . --ext=.ts",
|
|
32
|
-
"format": "prettier --write .",
|
|
33
|
-
"prebuild": "npm run lint && npm run clean",
|
|
34
|
-
"build": "npm run clean && tsc",
|
|
35
|
-
"release": "release-it",
|
|
36
42
|
"version": "npm run build",
|
|
37
|
-
"prepublishOnly": "npm run build"
|
|
43
|
+
"prepublishOnly": "npm run build",
|
|
44
|
+
"release": "np"
|
|
38
45
|
},
|
|
39
46
|
"devDependencies": {
|
|
40
47
|
"@adonisjs/assembler": "^7.7.0",
|