From b957c6e3a59d0f47a2133055a26d34d8ce4d2936 Mon Sep 17 00:00:00 2001 From: Harminder virk Date: Mon, 7 Oct 2019 15:56:53 +0530 Subject: [PATCH] feat: implement updateOrCreate closes #484 --- adonis-typings/model.ts | 10 +++ src/Orm/BaseModel/index.ts | 35 +++++++++- test-helpers/tmp/db.sqlite | Bin 0 -> 45056 bytes test/orm/base-model.spec.ts | 125 ++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 test-helpers/tmp/db.sqlite diff --git a/adonis-typings/model.ts b/adonis-typings/model.ts index d12065cb..e8ec146a 100644 --- a/adonis-typings/model.ts +++ b/adonis-typings/model.ts @@ -688,6 +688,16 @@ declare module '@ioc:Adonis/Lucid/Model' { options?: ModelAdapterOptions, ): Promise> + /** + * Returns the first row or save it to the database + */ + updateOrCreate ( + this: T, + search: any, + updatePayload: any, + options?: ModelAdapterOptions, + ): Promise> + /** * Fetch all rows */ diff --git a/src/Orm/BaseModel/index.ts b/src/Orm/BaseModel/index.ts index 9dd0c3d8..35794f90 100644 --- a/src/Orm/BaseModel/index.ts +++ b/src/Orm/BaseModel/index.ts @@ -419,13 +419,44 @@ export class BaseModel implements ModelContract { if (!row) { row = new this() as InstanceType - row.$options = query.clientOptions row.fill(Object.assign({}, search, savePayload)) - return row } + /** + * Copying options from the select query client and use the same + * one's for persistance + */ + if (query.client.isTransaction) { + row.$trx = query.client as TransactionClientContract + } else { + row.$options = query.clientOptions + } + + return row + } + + /** + * Updates or creates a new row inside the database + */ + public static async updateOrCreate ( + this: T, + search: any, + updatedPayload: any, + options?: ModelAdapterOptions, + ) { + const row = await this.firstOrNew(search, updatedPayload, options) + + /** + * Update if row was found + */ + if (row.$persisted) { + row.merge(updatedPayload) + } + + await row.save() return row } + /** * Create a array of model instances from the adapter result */ diff --git a/test-helpers/tmp/db.sqlite b/test-helpers/tmp/db.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..7f3836c431f98647548431427405cfe99fc3550c GIT binary patch literal 45056 zcmeI(OK#IZ7{GBmZQ73TUF-Dj($C`G(QVm+00Iag zfB*srOijRXs(bFmi}r9U@!t7TwPlc~M9FxvHviy7{obp(cy;gole%b5HZ;Y$e$^Bz zNaU7`L=Yw-==;9tMXKvX1JRKK;q{YH1+7SS^`0+_W-kgil`k6y8tU5dP6v&^>&m9s z_M+B%FIvBL^X7)$s#|-BY&X25DcX81QC+F4`#tj_U%lZt^S|Bm=j~xPO=x_$ID0;k zPbRA_l}bOBQ6Z1EihI6ySQ`5}$INHvPHi#G=W&`(E9@G`ID1~s=aW^J%BL5`Ng0TN^u`^@anmqf6KLZ&Z?j-Kc%Nldh!^MMn6y= z`!cPpj-!74^wIOzb@Axw!}=S2eKV(7@%>E^2IEa8C-a_^cjH-Q!M$|J9*wj3qpxSO zjacd<1T9&3IhR8!Y+N_HyR2Jo=$p)UGj>1UPO;N)(uYc}{V2{-z@zRN6fQdZITh~% zvg;}Tugg(S-(M1aVrI9Yw%qjkej?Vct{t48Ow5hOzr;4ju1cq0R#|0009IL zKmY**5J2F+3!Jo { assert.equal(user!.email, 'nikk@gmail.com') assert.equal(user!.username, 'nikk') }) + + test('update the existing row when search criteria matches', async (assert) => { + class User extends BaseModel { + @column({ primary: true }) + public id: number + + @column() + public username: string + + @column() + public email: string + + @column() + public points: number + } + + await db.insertQuery().table('users').insert({ username: 'virk' }) + const user = await User.updateOrCreate({ username: 'virk' }, { points: 20 }) + assert.isTrue(user.$persisted) + assert.equal(user.points, 20) + assert.equal(user.username, 'virk') + + const users = await db.query().from('users') + + assert.lengthOf(users, 1) + assert.equal(users[0].points, 20) + }) + + test('execute updateOrCreate update action inside a transaction', async (assert) => { + class User extends BaseModel { + @column({ primary: true }) + public id: number + + @column() + public username: string + + @column() + public email: string + + @column() + public points: number + } + + await db.insertQuery().table('users').insert({ username: 'virk' }) + const trx = await db.transaction() + + const user = await User.updateOrCreate({ username: 'virk' }, { points: 20 }, { client: trx }) + + assert.isTrue(user.$persisted) + assert.equal(user.points, 20) + assert.equal(user.username, 'virk') + + await trx.rollback() + + const users = await db.query().from('users') + assert.lengthOf(users, 1) + + assert.equal(users[0].username, 'virk') + assert.equal(users[0].points, 0) + }) + + test('create a new row when search criteria fails', async (assert) => { + class User extends BaseModel { + @column({ primary: true }) + public id: number + + @column() + public username: string + + @column() + public email: string + + @column() + public points: number + } + + await db.insertQuery().table('users').insert({ username: 'virk' }) + const user = await User.updateOrCreate({ username: 'nikk' }, { points: 20 }) + + assert.isTrue(user.$persisted) + assert.equal(user.points, 20) + assert.equal(user.username, 'nikk') + + const users = await db.query().from('users') + assert.lengthOf(users, 2) + + assert.equal(users[0].username, 'virk') + assert.equal(users[0].points, 0) + + assert.equal(users[1].username, 'nikk') + assert.equal(users[1].points, 20) + }) + + test('execute updateOrCreate create action inside a transaction', async (assert) => { + class User extends BaseModel { + @column({ primary: true }) + public id: number + + @column() + public username: string + + @column() + public email: string + + @column() + public points: number + } + + await db.insertQuery().table('users').insert({ username: 'virk' }) + const trx = await db.transaction() + + const user = await User.updateOrCreate({ username: 'nikk' }, { points: 20 }, { client: trx }) + + assert.isTrue(user.$persisted) + assert.equal(user.points, 20) + assert.equal(user.username, 'nikk') + + await trx.rollback() + + const users = await db.query().from('users') + assert.lengthOf(users, 1) + + assert.equal(users[0].username, 'virk') + assert.equal(users[0].points, 0) + }) }) test.group('Base Model | hooks', (group) => {