From 4a2adcd696430d06db5bda103635c2ef6dc74c44 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Wed, 4 Dec 2024 07:31:21 +0530 Subject: [PATCH] feat: add support for creating transaction client from model --- src/orm/base_model/index.ts | 20 +++++++++++++++++- src/types/model.ts | 15 +++++++++++++- test/orm/base_model.spec.ts | 41 +++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/orm/base_model/index.ts b/src/orm/base_model/index.ts index fc540599..6244fe5c 100644 --- a/src/orm/base_model/index.ts +++ b/src/orm/base_model/index.ts @@ -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, @@ -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 { + 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. diff --git a/src/types/model.ts b/src/types/model.ts index 7e698d17..db2d9459 100644 --- a/src/types/model.ts +++ b/src/types/model.ts @@ -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, @@ -1143,6 +1148,14 @@ export interface LucidModel { options?: ModelAdapterOptions ): ModelQueryBuilderContract + /** + * Returns transaction client from the model. It is same as + * calling "db.transaction" + */ + transaction( + options?: ModelAdapterOptions & { isolationLevel?: IsolationLevels } + ): Promise + /** * Truncate model table */ diff --git a/test/orm/base_model.spec.ts b/test/orm/base_model.spec.ts index a35709dd..6082709e 100644 --- a/test/orm/base_model.spec.ts +++ b/test/orm/base_model.spec.ts @@ -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) + }) +})