From 4f04995a86c17fb3228a414e1560ed96b132bac4 Mon Sep 17 00:00:00 2001 From: Harminder virk Date: Mon, 30 Sep 2019 13:08:42 +0530 Subject: [PATCH] refactor: adding new static methods for querying on the model This commit has some loose changes, which will be addressed after we improve and restructure the test to cover broad use cases --- adonis-typings/model.ts | 59 +++++++++++++++++++++++++++---- example/index.ts | 22 ++++++------ src/Orm/BaseModel/index.ts | 66 ++++++++++++++++++++++++++++++++--- src/Orm/QueryBuilder/index.ts | 13 +++++++ test/adapter.spec.ts | 27 ++------------ 5 files changed, 141 insertions(+), 46 deletions(-) diff --git a/adonis-typings/model.ts b/adonis-typings/model.ts index 82e8c6f7..e501a24b 100644 --- a/adonis-typings/model.ts +++ b/adonis-typings/model.ts @@ -138,6 +138,11 @@ declare module '@ioc:Adonis/Lucid/Model' { */ first (): Promise | null> + /** + * Return the first matching row or fail + */ + firstOrFail (): Promise> + /** * Define relationships to be preloaded */ @@ -317,22 +322,64 @@ declare module '@ioc:Adonis/Lucid/Model' { /** * Creating model */ - create (this: T, values: ModelObject): InstanceType + create ( + this: T, + values: ModelObject, + options?: ModelOptions, + ): InstanceType /** - * Creating model by invoking actions on adapter + * Find one using the primary key */ - findBy ( + find ( this: T, - key: string, value: any, options?: ModelOptions, ): Promise> /** - * Fetch all rows and convert them to model instances + * Find one using the primary key or fail + */ + findOrFail ( + this: T, + value: any, + options?: ModelOptions, + ): Promise> + + /** + * Find many using an array of primary keys + */ + findMany ( + this: T, + value: any[], + options?: ModelOptions, + ): Promise[]> + + /** + * Returns the first row or save it to the database + */ + firstOrSave ( + this: T, + search: any, + savePayload?: any, + options?: ModelOptions, + ): Promise> + + /** + * Returns the first row or create a new instance of model without + * persisting it + */ + firstOrNew ( + this: T, + search: any, + savePayload?: any, + options?: ModelOptions, + ): Promise> + + /** + * Fetch all rows */ - findAll (this: T, options?: ModelOptions): Promise[]> + all (this: T, options?: ModelOptions): Promise[]> /** * Returns the query for fetching a model instance diff --git a/example/index.ts b/example/index.ts index cfc74c7d..3454598f 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,14 +1,14 @@ -import { BaseModel } from '@ioc:Adonis/Lucid/Orm' +// import { BaseModel } from '@ioc:Adonis/Lucid/Orm' +// import Database from '@ioc:Adonis/Lucid/Database' -class Profile extends BaseModel { -} +// class Profile extends BaseModel { +// } -class User extends BaseModel { - public username: string +// class User extends BaseModel { +// public username: string +// public profile: Profile[] +// } - public profile: Profile -} - -const user = new User() -const profile = user.$getRelated('profile') -console.log(profile) +// user.saveRelated('profile', new Profile()) +// const profile = user.$getRelated('profile') +// console.log(profile) diff --git a/src/Orm/BaseModel/index.ts b/src/Orm/BaseModel/index.ts index ee1887b2..98d59ebc 100644 --- a/src/Orm/BaseModel/index.ts +++ b/src/Orm/BaseModel/index.ts @@ -299,19 +299,77 @@ export class BaseModel implements ModelContract { /** * Find model instance using a key/value pair */ - public static async findBy ( + public static async find ( this: T, - key: string, value: any, options?: any, ) { - return this.query(options).where(key, value).first() + return this.query(options).where(this.$primaryKey, value).first() } + /** + * Find model instance using a key/value pair + */ + public static async findOrFail ( + this: T, + value: any, + options?: any, + ) { + return this.query(options).where(this.$primaryKey, value).firstOrFail() + } + + /** + * Find model instance using a key/value pair + */ + public static async findMany ( + this: T, + value: any[], + options?: any, + ) { + return this.query(options).whereIn(this.$primaryKey, value).exec() + } + + /** + * Find model instance using a key/value pair + */ + public static async firstOrSave ( + this: T, + search: any, + savePayload?: any, + options?: ModelOptions, + ) { + const row = await this.firstOrNew(search, savePayload, options) + if (!row.$persisted) { + await row.save() + } + + return row + } + + /** + * Find model instance using a key/value pair + */ + public static async firstOrNew ( + this: T, + search: any, + savePayload?: any, + options?: ModelOptions, + ) { + let row = await this.query(options).where(search).first() + + if (!row) { + row = new this() as InstanceType + row.$options = options + row.fill(Object.assign({}, search, savePayload)) + return row + } + + return row + } /** * Create a array of model instances from the adapter result */ - public static async findAll ( + public static async all ( this: T, options?: any, ) { diff --git a/src/Orm/QueryBuilder/index.ts b/src/Orm/QueryBuilder/index.ts index 7bbcc4e0..86e7ae39 100644 --- a/src/Orm/QueryBuilder/index.ts +++ b/src/Orm/QueryBuilder/index.ts @@ -159,6 +159,19 @@ export class ModelQueryBuilder extends Chainable implements ModelQueryBuilderCon return result[0] || null } + /** + * Fetch and return first results from the results set. This method + * will implicitly set a `limit` on the query + */ + public async firstOrFail (): Promise { + const result = await this.limit(1)['exec']() + if (!result.length) { + throw new Error('Row not found') + } + + return result[0] + } + /** * Define a relationship to be preloaded */ diff --git a/test/adapter.spec.ts b/test/adapter.spec.ts index e9844e11..c9f4fb39 100644 --- a/test/adapter.spec.ts +++ b/test/adapter.spec.ts @@ -110,30 +110,7 @@ test.group('Adapter', (group) => { assert.lengthOf(users, 0) }) - test('get model instance using the find call', async (assert) => { - const db = getDb() - const BaseModel = getBaseModel(ormAdapter()) - - class User extends BaseModel { - public static $table = 'users' - - @column({ primary: true }) - public id: number - - @column() - public username: string - } - User.$boot() - - const [id] = await db.table('users').returning('id').insert({ username: 'virk' }) - - const user = await User.findBy('username', 'virk') - assert.instanceOf(user, User) - assert.isFalse(user!.$isDirty) - assert.deepEqual(user!.$attributes, { id: id, username: 'virk' }) - }) - - test('get array of model instances using the findAll call', async (assert) => { + test('get array of model instances using the all call', async (assert) => { const db = getDb() const BaseModel = getBaseModel(ormAdapter()) @@ -152,7 +129,7 @@ test.group('Adapter', (group) => { [{ username: 'virk' }, { username: 'nikk' }], ) - const users = await User.findAll() + const users = await User.all() assert.lengthOf(users, 2) assert.instanceOf(users[0], User)