@codenameryuu/adonis-lucid-auto-preload 2.1.0 → 2.3.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
@@ -1,23 +1,35 @@
1
1
  # @codenameryuu/adonis-lucid-auto-preload
2
2
 
3
- Auto-preload multiple relationships when retrieving Lucid models on Adonis JS 7.
3
+ Auto-preload (eager loading) multiple relationships when retrieving Lucid models on Adonis JS.
4
4
 
5
5
  ## Requirement
6
6
 
7
7
  * Adonis Js 7
8
+ * Lucid 22 or higher
8
9
 
9
10
  ## Installation
10
11
 
12
+ * Install the package
13
+
11
14
  ```bash
12
15
  yarn add @codenameryuu/adonis-lucid-auto-preload
13
16
  ```
14
17
 
15
- ## Configure
18
+ * Configure the package
16
19
 
17
20
  ```bash
18
21
  node ace configure @codenameryuu/adonis-lucid-auto-preload
19
22
  ```
20
23
 
24
+ * Make sure to register the provider inside `adonisrc.ts` file.
25
+
26
+ ```typescript
27
+ providers: [
28
+ // ...
29
+ () => import('@codenameryuu/adonis-lucid-auto-preload/provider'),
30
+ ],
31
+ ```
32
+
21
33
  ## Usage
22
34
 
23
35
  Extend from the AutoPreload mixin and add a new `static $with` attribute.
@@ -26,40 +38,50 @@ Adding `as const` to `$with` array will let the compiler know about your relatio
26
38
 
27
39
  Relationships will be auto-preloaded for `find` , `all` and `paginate` queries.
28
40
 
41
+ **Note:** Relationships must be `belongsTo` , if you are using other relationship types, it may cause infinite loop.
42
+
29
43
  ### **Using relation name**
30
44
 
31
45
  ```typescript
32
- // App/Models/User.ts
46
+ // App/Models/Product.ts
33
47
 
34
- import { BaseModel, column, hasMany, HasMany } from '@adonisjs/lucid/orm'
35
- import { compose } from '@adonisjs/core/helpers'
48
+ import { BaseModel, column, belongsTo } from '@adonisjs/lucid/orm'
49
+ import type { BelongsTo } from "@adonisjs/lucid/types/relations";
50
+ import { compose } from "@adonisjs/core/helpers";
36
51
 
37
- import { AutoPreload } from '@codenameryuu/adonis-lucid-auto-preload'
52
+ import { AutoPreload } from "@codenameryuu/adonis-lucid-auto-preload";
38
53
 
39
- import Post from '#models/post'
54
+ import ProductCategory from '#models/product-category'
40
55
 
41
- class User extends compose(BaseModel, AutoPreload) {
42
- public static $with = ['posts'] as const
56
+ class Product extends compose(BaseModel, AutoPreload) {
57
+ public static $with = ['productCategory'] as const
43
58
 
44
59
  @column({ isPrimary: true })
45
60
  public id: number
46
61
 
47
62
  @column()
48
- public email: string
63
+ declare productCategoryId: number
49
64
 
50
- @hasMany(() => Post)
51
- public posts: HasMany<typeof Post>
65
+ @column()
66
+ declare name: string
67
+
68
+ @belongsTo(() => ProductCategory, {
69
+ localKey: "id",
70
+ foreignKey: "product_category_id",
71
+ serializeAs: "product_category",
72
+ })
73
+ declare productCategory: BelongsTo<typeof ProductCategory>;
52
74
  }
53
75
  ```
54
76
 
55
77
  ```typescript
56
- // App/Controllers/Http/UsersController.ts
78
+ // App/Controllers/Http/ProductsController.ts
57
79
 
58
- import User from '#models/user'
80
+ import Product from '#models/product'
59
81
 
60
- export default class UsersController {
82
+ export default class ProductsController {
61
83
  public async show() {
62
- return await User.find(1) // ⬅ Returns user with posts attached.
84
+ return await Product.find(1) // ⬅ Returns product with product category attached.
63
85
  }
64
86
  }
65
87
  ```
@@ -69,106 +91,131 @@ export default class UsersController {
69
91
  You can also use functions to auto-preload relationships. The function will receive the model query builder as the only argument.
70
92
 
71
93
  ```typescript
72
- // App/Models/User.ts
94
+ // App/Models/Product.ts
73
95
 
74
- import { BaseModel, column, hasMany, HasMany } from '@adonisjs/lucid/orm'
96
+ import { BaseModel, column, belongsTo } from '@adonisjs/lucid/orm'
97
+ import type { BelongsTo } from "@adonisjs/lucid/types/relations";
75
98
  import type { ModelQueryBuilderContract } from '@adonisjs/lucid/types/model'
76
99
  import { compose } from '@adonisjs/core/helpers'
77
100
 
78
101
  import { AutoPreload } from '@codenameryuu/adonis-lucid-auto-preload'
79
102
 
80
- import Post from '#models/post'
103
+ import ProductCategory from '#models/product-category'
81
104
 
82
- class User extends compose(BaseModel, AutoPreload) {
105
+ class Product extends compose(BaseModel, AutoPreload) {
83
106
  public static $with = [
84
107
  (query: ModelQueryBuilderContract<typeof this>) => {
85
- query.preload('posts')
108
+ query.preload('productCategory')
86
109
  }
87
110
  ]
88
111
 
89
112
  @column({ isPrimary: true })
90
- public id: number
113
+ declare id: number
91
114
 
92
115
  @column()
93
- public email: string
116
+ declare productCategoryId: number
94
117
 
95
- @hasMany(() => Post)
96
- public posts: HasMany<typeof Post>
118
+ @column()
119
+ declare name: string
120
+
121
+ @belongsTo(() => ProductCategory, {
122
+ localKey: "id",
123
+ foreignKey: "product_category_id",
124
+ serializeAs: "product_category",
125
+ })
126
+ declare productCategory: BelongsTo<typeof ProductCategory>;
97
127
  }
98
128
  ```
99
129
 
100
130
  ```typescript
101
- // App/Controllers/Http/UsersController.ts
131
+ // App/Controllers/Http/ProductsController.ts
102
132
 
103
- import User from '#models/user'
133
+ import Product from '#models/product'
104
134
 
105
- export default class UsersController {
135
+ export default class ProductsController {
106
136
  public async show() {
107
- return await User.find(1) // ⬅ Returns user with posts attached.
137
+ return await Product.find(1) // ⬅ Returns product with product category attached.
108
138
  }
109
139
  }
110
140
  ```
111
141
 
112
142
  ## Nested relationships
113
143
 
114
- You can auto-preload nested relationships using the dot "." between the parent model and the child model. In the following example, `User` -> hasMany -> `Post` -> hasMany -> `Comment` .
144
+ You can auto-preload nested relationships using the dot "." between the parent model and the child model. In the following example, `Product` -> belongsTo -> `ProductCategory` -> belongsTo -> `User` .
115
145
 
116
146
  ```typescript
117
- // App/Models/Post.ts
147
+ // App/Models/ProductCategory.ts
118
148
 
119
- import { BaseModel, column, hasMany, HasMany } from '@adonisjs/lucid/orm'
149
+ import { BaseModel, column, belongsTo } from '@adonisjs/lucid/orm'
150
+ import type { BelongsTo } from "@adonisjs/lucid/types/relations";
151
+ import { compose } from "@adonisjs/core/helpers";
120
152
 
121
- class Post extends BaseModel {
122
- @column({ isPrimary: true })
123
- public id: number
153
+ import { AutoPreload } from "@codenameryuu/adonis-lucid-auto-preload";
124
154
 
125
- @column()
126
- public userId: number
155
+ import User from '#models/user'
127
156
 
128
- @column()
129
- public title: string
157
+ class ProductCategory extends compose(BaseModel, AutoPreload) {
158
+ public static $with = ['user'] as const
159
+
160
+ @column({ isPrimary: true })
161
+ declare id: number
130
162
 
131
163
  @column()
132
- public content: string
164
+ declare userId: number
133
165
 
134
- @hasMany(() => Comment)
135
- public comments: HasMany<typeof Comment>
166
+ @column()
167
+ declare name: string
168
+
169
+ @belongsTo(() => User, {
170
+ localKey: "id",
171
+ foreignKey: "user_id",
172
+ serializeAs: "user",
173
+ })
174
+ declare user: BelongsTo<typeof User>;
136
175
  }
137
176
  ```
138
177
 
139
178
  ```typescript
140
- // App/Models/User.ts
179
+ // App/Models/Product.ts
141
180
 
142
- import { BaseModel, column, hasMany, HasMany } from '@adonisjs/lucid/orm'
181
+ import { BaseModel, column, belongsTo } from '@adonisjs/lucid/orm'
182
+ import type { BelongsTo } from "@adonisjs/lucid/types/relations";
143
183
  import { compose } from '@adonisjs/core/helpers'
144
184
 
145
185
  import { AutoPreload } from '@codenameryuu/adonis-lucid-auto-preload'
146
186
 
147
- import Post from '#models/post'
187
+ import ProductCategory from '#models/product-category'
148
188
 
149
- class User extends compose(BaseModel, AutoPreload) {
150
- public static $with = ['posts.comments'] as const
189
+ class Product extends compose(BaseModel, AutoPreload) {
190
+ public static $with = ['productCategory.user'] as const
151
191
 
152
192
  @column({ isPrimary: true })
153
- public id: number
193
+ declare id: number
154
194
 
155
195
  @column()
156
- public email: string
196
+ declare productCategoryId: number
157
197
 
158
- @hasMany(() => Post)
159
- public posts: HasMany<typeof Post>
198
+ @column()
199
+ declare name: string
200
+
201
+ @belongsTo(() => ProductCategory, {
202
+ localKey: "id",
203
+ foreignKey: "product_category_id",
204
+ serializeAs: "product_category",
205
+ })
206
+ declare productCategory: BelongsTo<typeof ProductCategory>;
160
207
  }
161
208
  ```
162
209
 
163
- When retrieving a user, it will preload both `posts` and `comments` ( `comments` will be attached to their `posts` parents objects).
210
+ When retrieving a product, it will preload both `productCategory` and `user` ( `user` will be attached to their `productCategory` parents objects).
164
211
 
165
212
  You can also use functions to auto-preload nested relationships.
166
213
 
167
214
  ```typescript
168
215
  public static $with = [
169
216
  (query: ModelQueryBuilderContract<typeof this>) => {
170
- query.preload('posts', (postsQuery) => {
171
- postsQuery.preload('comments')
217
+ query.preload('productCategory', (productCategoryQuery) => {
218
+ productCategoryQuery.preload('user')
172
219
  })
173
220
  }
174
221
  ]
@@ -180,46 +227,18 @@ The `AutoPreload` mixin will add 3 methods to your models. We will explain all o
180
227
 
181
228
  We will use the following model for our methods examples.
182
229
 
183
- ```typescript
184
- // App/Models/User.ts
185
-
186
- import { BaseModel, column, hasOne, HasOne, hasMany, HasMany } from '@adonisjs/lucid/orm'
187
- import { compose } from '@adonisjs/core/helpers'
188
-
189
- import { AutoPreload } from '@codenameryuu/adonis-lucid-auto-preload'
190
-
191
- import Profile from '#models/profile'
192
- import Post from '#models/post'
193
-
194
- class User extends compose(BaseModel, AutoPreload) {
195
- public static $with = ['posts', 'profile'] as const
196
-
197
- @column({ isPrimary: true })
198
- public id: number
199
-
200
- @column()
201
- public email: string
202
-
203
- @hasOne(() => Profile)
204
- public profile: HasOne<typeof Profile>
205
-
206
- @hasMany(() => Post)
207
- public posts: HasMany<typeof Post>
208
- }
209
- ```
210
-
211
230
  ### **without**
212
231
 
213
232
  This method takes an array of relationship names as the only argument. All specified relationships will not be auto-preloaded. You cannot specify relationships registered using functions.
214
233
 
215
234
  ```typescript
216
- // App/Controllers/Http/UsersController.ts
235
+ // App/Controllers/Http/ProductsController.ts
217
236
 
218
- import User from '#models/user'
237
+ import Product from '#models/product'
219
238
 
220
- export default class UsersController {
239
+ export default class ProductsController {
221
240
  public async show() {
222
- return await User.without(['posts']).find(1) // ⬅ Returns user with profile and without posts.
241
+ return await Product.without(['productCategory']).find(1) // ⬅ Returns product without product category.
223
242
  }
224
243
  }
225
244
  ```
@@ -229,13 +248,13 @@ export default class UsersController {
229
248
  This method takes an array of relationship names as the only argument. Only specified relationships will be auto-preloaded. You cannot specify relationships registered using functions.
230
249
 
231
250
  ```typescript
232
- // App/Controllers/Http/UsersController.ts
251
+ // App/Controllers/Http/ProductsController.ts
233
252
 
234
- import User from '#models/user'
253
+ import Product from '#models/product'
235
254
 
236
- export default class UsersController {
255
+ export default class ProductsController {
237
256
  public async show() {
238
- return await User.withOnly(['profile']).find(1) // ⬅ Returns user with profile and without posts.
257
+ return await Product.withOnly(['productCategory']).find(1) // ⬅ Returns product with product category.
239
258
  }
240
259
  }
241
260
  ```
@@ -245,70 +264,20 @@ export default class UsersController {
245
264
  Exclude all relationships from being auto-preloaded.
246
265
 
247
266
  ```typescript
248
- // App/Controllers/Http/UsersController.ts
267
+ // App/Controllers/Http/ProductsController.ts
249
268
 
250
- import User from '#models/user'
269
+ import Product from '#models/product'
251
270
 
252
- export default class UsersController {
271
+ export default class ProductsController {
253
272
  public async show() {
254
- return await User.withoutAny().find(1) // ⬅ Returns user without profile and posts.
273
+ return await Product.withoutAny().find(1) // ⬅ Returns product without product category.
255
274
  }
256
275
  }
257
276
  ```
258
277
 
259
278
  > **Note**
260
279
  >
261
- > You can chain other model methods with mixin methods. For example, `await User.withoutAny().query().paginate(1)`
262
-
263
- ## **Limitations**
264
-
265
- * Consider the following scenario: `User` -> hasMany -> `Post` -> hasMany -> `Comments`. If you auto-preload `user` and `comments` from `Post` and you auto-preload `posts` from `User`, you will end-up in a infinite loop and your application will stop working.
266
-
267
- ## **Route model binding**
268
-
269
- When using route model binding, you cannot use `without` , `withOnly` and `withoutAny` methods in your controller. But, you can make use of [findForRequest](https://github.com/adonisjs/route-model-binding#change-lookup-logic) method.
270
-
271
- ```typescript
272
- // App/Models/User.ts
273
-
274
- import { BaseModel, column, hasOne, HasOne, hasMany, HasMany } from '@adonisjs/lucid/orm'
275
- import { compose } from '@adonisjs/core/helpers'
276
-
277
- import { AutoPreload } from '@codenameryuu/adonis-lucid-auto-preload'
278
-
279
- import Profile from '#models/profile'
280
- import Post from '#models/post'
281
-
282
- class User extends compose(BaseModel, AutoPreload) {
283
- public static $with = ['posts', 'profile'] as const
284
-
285
- @column({ isPrimary: true })
286
- public id: number
287
-
288
- @column()
289
- public email: string
290
-
291
- @hasOne(() => Profile)
292
- public profile: HasOne<typeof Profile>
293
-
294
- @hasMany(() => Post)
295
- public posts: HasMany<typeof Post>
296
-
297
- public static findForRequest(ctx, param, value) {
298
- const lookupKey = param.lookupKey === '$primaryKey' ? 'id' : param.lookupKey
299
-
300
- return this
301
- .without(['posts']) // ⬅ Do not auto-preload posts when using route model binding.
302
- .query()
303
- .where(lookupKey, value)
304
- .firstOrFail()
305
- }
306
- }
307
- ```
308
-
309
- ## Contributing
310
-
311
- Contributions, issues and feature requests are welcome!<br />Feel free to check [issues page](https://github.com/codenameryuu/adonis-lucid-auto-preload/issues). You can also take a look at the [contributing guide](https://github.com/codenameryuu/adonis-lucid-auto-preload/blob/master/CONTRIBUTING.md).
280
+ > You can chain other model methods with mixin methods. For example, `await Product.withoutAny().query().paginate(1)`
312
281
 
313
282
  ## License
314
283
 
@@ -65,6 +65,7 @@ export declare function AutoPreload<T extends NormalizeConstructor<LucidModel>>(
65
65
  * List of relationships to auto-preload.
66
66
  */
67
67
  $with: ReadonlyArray<PreloadEntry>;
68
+ boot(): void;
68
69
  beforeFindHook(query: ModelQueryBuilderContract<any, any>): void;
69
70
  beforeFetchHook(query: ModelQueryBuilderContract<any, any>): void;
70
71
  beforePaginateHook(queries: any): void;
@@ -114,7 +115,6 @@ export declare function AutoPreload<T extends NormalizeConstructor<LucidModel>>(
114
115
  <Model extends LucidModel>(this: Model, name: string): import("@adonisjs/lucid/types/relations").RelationshipsContract;
115
116
  };
116
117
  $defineProperty: <Model extends LucidModel, Prop extends keyof Model>(this: Model, propertyName: Prop, defaultValue: Model[Prop], strategy: "inherit" | "define" | ((value: Model[Prop]) => Model[Prop])) => void;
117
- boot: () => void;
118
118
  before: {
119
119
  <Model extends LucidModel, Event extends "find" | "fetch">(this: Model, event: Event, handler: import("@adonisjs/lucid/types/model").HooksHandler<ModelQueryBuilderContract<Model>, Event>): void;
120
120
  <Model extends LucidModel>(this: Model, event: "paginate", handler: import("@adonisjs/lucid/types/model").HooksHandler<[ModelQueryBuilderContract<Model>, ModelQueryBuilderContract<Model>], "paginate">): void;
@@ -1,10 +1,22 @@
1
- import { beforeFetch, beforeFind, beforePaginate } from '@adonisjs/lucid/orm';
2
1
  export function AutoPreload(superclass) {
3
2
  class AutoPreloadModel extends superclass {
4
3
  /**
5
4
  * List of relationships to auto-preload.
6
5
  */
7
6
  static $with = [];
7
+ static boot() {
8
+ super.boot();
9
+ // `compose()` introduces an intermediate class. Depending on how Lucid
10
+ // handles boot flags, we might see `booted` as inherited and skip hook
11
+ // registration. Track it per subclass instead.
12
+ if (this.$autoPreloadHooksRegistered)
13
+ return;
14
+ this.$autoPreloadHooksRegistered = true;
15
+ // Register hooks on the actual model subclass.
16
+ this.before('find', this.beforeFindHook.bind(this));
17
+ this.before('fetch', this.beforeFetchHook.bind(this));
18
+ this.before('paginate', this.beforePaginateHook.bind(this));
19
+ }
8
20
  static beforeFindHook(query) {
9
21
  this.applyAutoPreload(query);
10
22
  }
@@ -72,10 +84,5 @@ export function AutoPreload(superclass) {
72
84
  return query;
73
85
  }
74
86
  }
75
- // Apply Lucid hooks decorators without using `@decorator` syntax
76
- // (some package tsconfigs disable the decorator syntax).
77
- beforeFind()(AutoPreloadModel, 'beforeFindHook');
78
- beforeFetch()(AutoPreloadModel, 'beforeFetchHook');
79
- beforePaginate()(AutoPreloadModel, 'beforePaginateHook');
80
87
  return AutoPreloadModel;
81
88
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codenameryuu/adonis-lucid-auto-preload",
3
- "version": "2.1.0",
4
- "description": "Auto-preload multiple relationships when retrieving Lucid models on Adonis JS 7",
3
+ "version": "2.3.0",
4
+ "description": "Auto-preload (eager loading) multiple relationships when retrieving Lucid models on Adonis JS",
5
5
  "author": "codenameryuu",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/codenameryuu/adonis-lucid-auto-preload#readme",
@@ -1,2 +0,0 @@
1
- declare const _default: never[];
2
- export default _default;
@@ -1,10 +0,0 @@
1
- /*
2
- |--------------------------------------------------------------------------
3
- | Exporting an array of commands
4
- |--------------------------------------------------------------------------
5
- |
6
- | This package currently ships without Ace commands. Keep the command
7
- | index in place and export an empty array to satisfy the expected API.
8
- |
9
- */
10
- export default [];