Skip to content

Commit

Permalink
feat(orm): add loadOnce to lazy load a relationship only once (#1052)
Browse files Browse the repository at this point in the history
  • Loading branch information
RomainLanz authored Sep 25, 2024
1 parent 870e317 commit 96df6a6
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/orm/base_model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,16 @@ class BaseModelImpl implements LucidRow {
.processAllForOne(this, queryClient)
}

/**
* Load relationships onto the instance, but only if they are not
* already preloaded
*/
async loadOnce(relationName: any) {
if (!this.$preloaded[relationName]) {
return this.load(relationName)
}
}

/**
* @deprecated
*/
Expand Down
10 changes: 10 additions & 0 deletions src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
WhereHas,
WithAggregate,
WithCount,
PreloadWithoutCallback,
} from './relations.js'

/**
Expand Down Expand Up @@ -314,6 +315,9 @@ export interface LucidRowPreload<Model extends LucidRow> extends Preload<Model,
(callback: (preloader: PreloaderContract<Model>) => void): Promise<void>
}

export interface LucidRowPreloadOnce<Model extends LucidRow>
extends PreloadWithoutCallback<Model, Promise<void>> {}

export interface LucidRowAggregate<Model extends LucidRow> extends Preload<Model, Promise<void>> {
(callback: (preloader: PreloaderContract<Model>) => void): Promise<void>
}
Expand Down Expand Up @@ -643,6 +647,12 @@ export interface LucidRow {
*/
load: LucidRowPreload<this>

/**
* Load relationships onto the instance, but only if they are not
* already preloaded
*/
loadOnce: LucidRowPreloadOnce<this>

/**
* Alias for "load"
* @deprecated
Expand Down
4 changes: 4 additions & 0 deletions src/types/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,10 @@ export interface Preload<Model extends LucidRow, Builder> {
): Builder
}

export interface PreloadWithoutCallback<Model extends LucidRow, Builder> {
<Name extends ExtractModelRelations<Model>>(relation: Name): Builder
}

/**
* Shape of the preloader to preload relationships
*/
Expand Down
61 changes: 61 additions & 0 deletions test/orm/model_belongs_to.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,67 @@ test.group('Model | BelongsTo | preload', (group) => {
assert.equal(profile.user.id, profile.userId)
})

test('preload once using model instance', async ({ assert, fs }) => {
const app = new AppFactory().create(fs.baseUrl, () => {})
await app.init()
const db = getDb()
const adapter = ormAdapter(db)
const BaseModel = getBaseModel(adapter)

let queryCount = 0

class User extends BaseModel {
@column({ isPrimary: true })
declare id: number
}

class Profile extends BaseModel {
@column({ isPrimary: true })
declare id: number

@column()
declare userId: number

@column()
declare displayName: string

@belongsTo(() => User, {
onQuery() {
queryCount++
},
})
declare user: BelongsTo<typeof User>
}

await db
.insertQuery()
.table('users')
.insert([{ username: 'virk' }])

const users = await db.query().from('users')
await db
.insertQuery()
.table('profiles')
.insert([
{
user_id: users[0].id,
display_name: 'virk',
},
{
user_id: users[0].id,
display_name: 'virk',
},
])

const profile = await Profile.findOrFail(1)
await profile.loadOnce('user')
await profile.loadOnce('user')

assert.instanceOf(profile.user, User)
assert.equal(profile.user.id, profile.userId)
assert.equal(queryCount, 1)
})

test('preload nested relations', async ({ assert, fs }) => {
const app = new AppFactory().create(fs.baseUrl, () => {})
await app.init()
Expand Down

0 comments on commit 96df6a6

Please sign in to comment.