@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 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
- ## Introduction
5
+ ## Requirement
6
6
 
7
- Example, we want to return a list of users filtered by multiple parameters. When we navigate to:
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
- - Install the package
12
+ * Install the package
63
13
 
64
14
  ```bash
65
15
  yarn add @codenameryuu/adonis-lucid-filter
66
16
  ```
67
17
 
68
- - Configure the package
18
+ * Configure the package
69
19
 
70
20
  ```bash
71
21
  node ace configure @codenameryuu/adonis-lucid-filter
72
22
  ```
73
23
 
74
- ## Usage
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
- ### Generating The Filter
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 user
42
+ node ace make:filter product
97
43
  ```
98
44
 
99
- Where `user` is the Lucid Model you are creating the filter for. This will create `app/models/filters/user_filter.js`
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
- - Empty strings are ignored
106
- - `setup()` will be called regardless of input
107
- - `_id` is dropped from the end of the input to define the method so filtering `user_id` would use the `user()` method
108
- - Input without a corresponding filter method are ignored
109
- - The value of the key is injected into the method
110
- - All values are accessible through the `this.$input` a property
111
- - All QueryBuilder methods are accessible in `this.$query` object in the model filter class.
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
- "companyId": 5,
118
- "name": "Tony",
119
- "mobilePhone": "888555"
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 User from '#models/user'
74
+ import Product from '#models/product'
129
75
 
130
- export default class UserFilter extends BaseModelFilter {
131
- declare $query: ModelQueryBuilderContract<typeof User>
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
- // This will filter 'companyId', 'company_id' OR 'company'
136
- company(id: number) {
137
- this.$query.where('company_id', id)
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((builder) => {
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
- mobilePhone(phone: string) {
147
- this.$query.where('mobile_phone', 'LIKE', `${phone}%`)
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
- export default class User extends compose(BaseModel, Filterable) {
205
- static $filter = () => UserFilter
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 User from '#models/user'
152
+ import Product from '#models/product'
216
153
 
217
- export default class UsersController {
218
- async index({ request }: HttpContext): Promise<User[]> {
219
- return User.filter(request.qs()).exec()
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<User>> {
225
- const { page = 1, ...input } = request.qs()
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 AdminFilter from '#models/filters/admin_filter'
239
- import UserFilter from '#models/filters/user_filter'
174
+ import ProductFilter from '#models/filters/product_filter'
175
+ import ProductExclusiveFilter from '#models/filters/product_exclusive_filter'
240
176
 
241
- export default class UsersController {
242
- async index({ request, auth }: HttpContext): Promise<User[]> {
243
- const filter = auth.user.isAdmin() ? AdminFilter : UserFilter
244
- return User.filter(request.qs(), filter).exec()
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 User from '#models/user'
191
+ import ProductCategory from '#models/product_category'
256
192
 
257
- export default class UserPostsController {
193
+ export default class ProductCategoriesController {
258
194
  /**
259
- * Get a list posts of user
260
- * GET /users/:user_id/posts
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<Post[]> {
263
- const user: User = await User.findOrFail(params.user_id)
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 user
266
- .related('posts')
203
+ return productCategory
204
+ .related('products')
267
205
  .query()
268
- .apply((scopes) => scopes.filtration(request.qs()))
269
- .exec()
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,4 @@
1
+ import { CommandMetaData, Command } from '@adonisjs/ace/types';
2
+
3
+ export function getMetaData(): Promise<CommandMetaData[]>
4
+ export function getCommand(metaData: CommandMetaData): Promise<Command | null>
@@ -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.0.0",
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",