Skip to content

Commit

Permalink
feat: add support for creating transaction client from model
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Dec 4, 2024
1 parent 35821eb commit 4a2adcd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 2 deletions.
20 changes: 19 additions & 1 deletion src/orm/base_model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { DateTime } from 'luxon'
import Hooks from '@poppinss/hooks'
import lodash from '@poppinss/utils/lodash'
import { Exception, defineStaticProperty } from '@poppinss/utils'
import { QueryClientContract, TransactionClientContract } from '../../types/database.js'
import {
IsolationLevels,
QueryClientContract,
TransactionClientContract,
} from '../../types/database.js'

import {
LucidRow,
Expand Down Expand Up @@ -241,6 +245,20 @@ class BaseModelImpl implements LucidRow {
return this.$adapter.query(this, options)
}

/**
* Returns the model query instance for the given model
*/
static async transaction(
options?: ModelAdapterOptions & { isolationLevel?: IsolationLevels }
): Promise<TransactionClientContract> {
const client = this.$adapter.modelConstructorClient(this, options)
if (client.isTransaction) {
return client as TransactionClientContract
}

return client.transaction()
}

/**
* Create a model instance from the adapter result. The result value must
* be a valid object, otherwise `null` is returned.
Expand Down
15 changes: 14 additions & 1 deletion src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@

import { DateTime } from 'luxon'
import type Hooks from '@poppinss/hooks'
import { DialectContract, QueryClientContract, TransactionClientContract } from './database.js'
import {
DialectContract,
IsolationLevels,
QueryClientContract,
TransactionClientContract,
} from './database.js'

import {
Update,
Expand Down Expand Up @@ -1143,6 +1148,14 @@ export interface LucidModel {
options?: ModelAdapterOptions
): ModelQueryBuilderContract<Model, Result>

/**
* Returns transaction client from the model. It is same as
* calling "db.transaction"
*/
transaction(
options?: ModelAdapterOptions & { isolationLevel?: IsolationLevels }
): Promise<TransactionClientContract>

/**
* Truncate model table
*/
Expand Down
41 changes: 41 additions & 0 deletions test/orm/base_model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8277,3 +8277,44 @@ test.group('Base Model | lockForUpdate', (group) => {
)
})
})

test.group('Base Model | transaction', (group) => {
group.setup(async () => {
await setup()
})

group.teardown(async () => {
await cleanupTables()
})

group.each.teardown(async () => {
await resetTables()
})

test('create transaction client using model', async ({ fs, assert }) => {
const app = new AppFactory().create(fs.baseUrl, () => {})
await app.init()
const db = getDb()
const adapter = ormAdapter(db)

const BaseModel = getBaseModel(adapter)

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

@column()
declare username: string

@column()
declare email: string
}

const client = await User.transaction()
await client.insertQuery().table('users').insert({ username: 'virk' })
await client.rollback()
const user = await User.find(1)

assert.isNull(user)
})
})

0 comments on commit 4a2adcd

Please sign in to comment.