Skip to content

Commit

Permalink
refactor: adding new static methods for querying on the model
Browse files Browse the repository at this point in the history
This commit has some loose changes, which will be addressed after
we improve and restructure the test to cover broad use cases
  • Loading branch information
thetutlage committed Sep 30, 2019
1 parent eb4de43 commit ee75a1d
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 46 deletions.
59 changes: 53 additions & 6 deletions adonis-typings/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ declare module '@ioc:Adonis/Lucid/Model' {
*/
first (): Promise<InstanceType<Model> | null>

/**
* Return the first matching row or fail
*/
firstOrFail (): Promise<InstanceType<Model>>

/**
* Define relationships to be preloaded
*/
Expand Down Expand Up @@ -317,22 +322,64 @@ declare module '@ioc:Adonis/Lucid/Model' {
/**
* Creating model
*/
create<T extends ModelConstructorContract> (this: T, values: ModelObject): InstanceType<T>
create<T extends ModelConstructorContract> (
this: T,
values: ModelObject,
options?: ModelOptions,
): InstanceType<T>

/**
* Creating model by invoking actions on adapter
* Find one using the primary key
*/
findBy<T extends ModelConstructorContract> (
find<T extends ModelConstructorContract> (
this: T,
key: string,
value: any,
options?: ModelOptions,
): Promise<null | InstanceType<T>>

/**
* Fetch all rows and convert them to model instances
* Find one using the primary key or fail
*/
findOrFail<T extends ModelConstructorContract> (
this: T,
value: any,
options?: ModelOptions,
): Promise<InstanceType<T>>

/**
* Find many using an array of primary keys
*/
findMany<T extends ModelConstructorContract> (
this: T,
value: any[],
options?: ModelOptions,
): Promise<InstanceType<T>[]>

/**
* Returns the first row or save it to the database
*/
firstOrSave<T extends ModelConstructorContract> (
this: T,
search: any,
savePayload?: any,
options?: ModelOptions,
): Promise<InstanceType<T>>

/**
* Returns the first row or create a new instance of model without
* persisting it
*/
firstOrNew<T extends ModelConstructorContract> (
this: T,
search: any,
savePayload?: any,
options?: ModelOptions,
): Promise<InstanceType<T>>

/**
* Fetch all rows
*/
findAll<T extends ModelConstructorContract> (this: T, options?: ModelOptions): Promise<InstanceType<T>[]>
all<T extends ModelConstructorContract> (this: T, options?: ModelOptions): Promise<InstanceType<T>[]>

/**
* Returns the query for fetching a model instance
Expand Down
22 changes: 11 additions & 11 deletions example/index.ts
Original file line number Diff line number Diff line change
@@ -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)
66 changes: 62 additions & 4 deletions src/Orm/BaseModel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,19 +299,77 @@ export class BaseModel implements ModelContract {
/**
* Find model instance using a key/value pair
*/
public static async findBy<T extends ModelConstructorContract> (
public static async find<T extends ModelConstructorContract> (
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<T extends ModelConstructorContract> (
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<T extends ModelConstructorContract> (
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<T extends ModelConstructorContract> (
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<T extends ModelConstructorContract> (
this: T,
search: any,
savePayload?: any,
options?: ModelOptions,
) {
let row = await this.query(options).where(search).first()

if (!row) {
row = new this() as InstanceType<T>
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 <T extends ModelConstructorContract> (
public static async all <T extends ModelConstructorContract> (
this: T,
options?: any,
) {
Expand Down
13 changes: 13 additions & 0 deletions src/Orm/QueryBuilder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> {
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
*/
Expand Down
27 changes: 2 additions & 25 deletions test/adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Expand All @@ -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)
Expand Down

0 comments on commit ee75a1d

Please sign in to comment.