Skip to content

Commit

Permalink
feat(relations): push relationships preloads using model instance
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Jan 12, 2020
1 parent b9b4b40 commit 82c4cfb
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 8 deletions.
1 change: 1 addition & 0 deletions adonis-typings/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ declare module '@ioc:Adonis/Lucid/Model' {
relatedModel (): ModelConstructorContract

setRelated (model: ModelContract, related?: ModelContract | ModelContract[] | null): void
pushRelated (model: ModelContract, related?: ModelContract | ModelContract[] | null): void
setRelatedMany (models: ModelContract[], related: ModelContract[]): void

getQuery (
Expand Down
4 changes: 2 additions & 2 deletions src/Orm/Preloader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,14 @@ export class Preloader implements PreloaderContract {
* Set only one when relationship is hasOne or belongsTo
*/
if (['hasOne', 'belongsTo'].includes(relation.relation.type)) {
relation.relation.setRelated(model, result[0])
relation.relation.pushRelated(model, result[0])
return
}

/**
* Set relationships on model
*/
relation.relation.setRelated(model, result)
relation.relation.pushRelated(model, result)
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/Orm/Relations/BelongsTo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,17 @@ export class BelongsTo implements RelationContract {
model.$setRelated(this.relationName as keyof typeof model, related)
}

/**
* Push the related model instance
*/
public pushRelated (model: ModelContract, related?: ModelContract) {
if (!related) {
return
}

model.$pushRelated(this.relationName as keyof typeof model, related)
}

/**
* Sets the related instances on the model
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Orm/Relations/HasMany/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export class HasMany extends HasOneOrMany {
*/
public setRelatedMany (parents: ModelContract[], related: ModelContract[]) {
parents.forEach((parent) => {
const relation = related.filter((model) => model[this.foreignKey] === parent[this.localKey])
this.setRelated(parent, relation)
const relations = related.filter((model) => model[this.foreignKey] === parent[this.localKey])
this.setRelated(parent, relations)
})
}
}
15 changes: 13 additions & 2 deletions src/Orm/Relations/HasManyThrough/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,26 @@ export class HasManyThrough implements RelationContract {
parent.$setRelated(this.relationName as keyof typeof parent, related)
}

/**
* Push the related model instance
*/
public pushRelated (parent: ModelContract, related?: ModelContract[]) {
if (!related) {
return
}

parent.$pushRelated(this.relationName as keyof typeof parent, related)
}

/**
* Set many related instances
*/
public setRelatedMany (parents: ModelContract[], related: ModelContract[]) {
parents.forEach((parent) => {
const relation = related.filter((model) => {
const relations = related.filter((model) => {
return model.$extras[`through_${this.foreignAdapterKey}`] === parent[this.localKey]
})
this.setRelated(parent, relation)
this.setRelated(parent, relations)
})
}
}
11 changes: 11 additions & 0 deletions src/Orm/Relations/HasOneOrMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,15 @@ export abstract class HasOneOrMany implements RelationContract {

parent.$setRelated(this.relationName as keyof typeof parent, related)
}

/**
* Push the related model instance
*/
public pushRelated (parent: ModelContract, related?: ModelContract | ModelContract[]) {
if (!related) {
return
}

parent.$pushRelated(this.relationName as keyof typeof parent, related)
}
}
16 changes: 14 additions & 2 deletions src/Orm/Relations/ManyToMany/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,18 +212,30 @@ export class ManyToMany implements RelationContract {
if (!related) {
return
}

model.$setRelated(this.relationName as keyof typeof model, related)
}

/**
* Push the related model instance
*/
public pushRelated (model: ModelContract, related?: ModelContract[] | null) {
if (!related) {
return
}

model.$pushRelated(this.relationName as keyof typeof model, related)
}

/**
* Must be implemented by parent class
*/
public setRelatedMany (parents: ModelContract[], related: ModelContract[]) {
parents.forEach((parent) => {
const relation = related.filter((model) => {
const relations = related.filter((model) => {
return parent[this.localKey] === model.$extras[this.pivotForeignKeyAlias]
})
this.setRelated(parent, relation)
this.setRelated(parent, relations)
})
}
}
Binary file removed test-helpers/tmp/db.sqlite
Binary file not shown.
69 changes: 69 additions & 0 deletions test/orm/model-has-many-through.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,75 @@ test.group('Model | Has Many Through', (group) => {
assert.equal(countries[1].posts[0].title, 'Adonis5')
assert.equal(countries[1].posts[0].$extras.through_country_id, 2)
})

test('push to existing relations when preloading using model instance', async (assert) => {
class User extends BaseModel {
@column({ primary: true })
public id: number

@column()
public countryId: number
}
User.$boot()

class Post extends BaseModel {
@column({ primary: true })
public id: number

@column()
public userId: number

@column()
public title: string
}
Post.$boot()

class Country extends BaseModel {
@column({ primary: true })
public id: number

@hasManyThrough([() => Post, () => User])
public posts: Post[]
}
Country.$boot()

await db.insertQuery().table('countries').insert([{ name: 'India' }, { name: 'USA' }])

await db.insertQuery().table('users').insert([
{ username: 'virk', country_id: 1 },
{ username: 'nikk', country_id: 2 },
])

await db.insertQuery().table('posts').insert([
{ title: 'Adonis 101', user_id: 1 },
{ title: 'Lucid 101', user_id: 1 },
{ title: 'Adonis5', user_id: 2 },
])

const countries = await Country.query().orderBy('id', 'asc')
assert.lengthOf(countries, 2)

const dummyPost = new Post()
dummyPost.fill({ userId: 1, title: 'Dummy 101' })
countries[0].$setRelated('posts', [dummyPost])

await countries[0].preload('posts')
await countries[1].preload('posts')

assert.lengthOf(countries[0].posts, 3)
assert.lengthOf(countries[1].posts, 1)

assert.equal(countries[0].posts[0].title, 'Dummy 101')

assert.equal(countries[0].posts[1].title, 'Adonis 101')
assert.equal(countries[0].posts[1].$extras.through_country_id, 1)

assert.equal(countries[0].posts[2].title, 'Lucid 101')
assert.equal(countries[0].posts[2].$extras.through_country_id, 1)

assert.equal(countries[1].posts[0].title, 'Adonis5')
assert.equal(countries[1].posts[0].$extras.through_country_id, 2)
})
})

test.group('Model | Has Many Through | fetch', (group) => {
Expand Down
53 changes: 53 additions & 0 deletions test/orm/model-has-many.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,59 @@ test.group('Model | HasMany', (group) => {
assert.equal(user!.posts[0].$options!.connection, 'secondary')
assert.equal(user!.posts[0].comments[0].$options!.connection, 'secondary')
})

test('push to existing relations when preloading using model instance', async (assert) => {
class Post extends BaseModel {
@column({ primary: true })
public id: number

@column()
public userId: number

@column()
public title: string
}

class User extends BaseModel {
@column({ primary: true })
public id: number

@hasMany(() => Post)
public posts: Post[]
}

await db.insertQuery().table('users').insert([{ username: 'virk' }, { username: 'nikk' }])
await db.insertQuery().table('posts').insert([
{
user_id: 1,
title: 'Adonis 101',
},
{
user_id: 1,
title: 'Lucid 101',
},
{
user_id: 2,
title: 'Lucid 102',
},
])

User.$boot()
const users = await User.query().orderBy('id', 'asc')

const dummyPost = new Post()
dummyPost.fill({ userId: users[0].id, title: 'Dummy 101' })
users[0].$setRelated('posts', [dummyPost])

await users[0].preload('posts')
await users[1].preload('posts')

assert.lengthOf(users[0]!.posts, 3)
assert.equal(users[0].posts[0].title, 'Dummy 101')

assert.lengthOf(users[1]!.posts, 1)
assert.equal(users[1].posts[0].title, 'Lucid 102')
})
})

test.group('Model | HasMany | fetch related', (group) => {
Expand Down
65 changes: 65 additions & 0 deletions test/orm/model-many-to-many.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,71 @@ test.group('Model | Many To Many', (group) => {
assert.equal(users[1].skills[0].$extras.pivot_skill_id, 2)
assert.equal(users[1].skills[0].$extras.pivot_proficiency, 'beginner')
})

test('push to existing relations when preloading using model instance', async (assert) => {
class Skill extends BaseModel {
@column({ primary: true })
public id: number

@column()
public name: string
}

class User extends BaseModel {
@column({ primary: true })
public id: number

@manyToMany(() => Skill)
public skills: Skill[]
}

User.$boot()
User.$getRelation('skills')!.boot()

await db.insertQuery().table('users').insert([{ username: 'virk' }, { username: 'nikk' }])
await db.insertQuery().table('skills').insert([{ name: 'Programming' }, { name: 'Dancing' }])
await db.insertQuery().table('skill_user').insert([
{
user_id: 1,
skill_id: 1,
},
{
user_id: 1,
skill_id: 2,
},
{
user_id: 2,
skill_id: 2,
},
])

const users = await User.query().orderBy('id', 'asc')
assert.lengthOf(users, 2)

const dummySkill = new Skill()
dummySkill.fill({ name: 'dummy' })
users[0].$setRelated('skills', [dummySkill])

await users[0].preload('skills')
await users[1].preload('skills')

assert.lengthOf(users[0].skills, 3)
assert.lengthOf(users[1].skills, 1)

assert.equal(users[0].skills[0].name, 'dummy')

assert.equal(users[0].skills[1].name, 'Programming')
assert.equal(users[0].skills[1].$extras.pivot_user_id, 1)
assert.equal(users[0].skills[1].$extras.pivot_skill_id, 1)

assert.equal(users[0].skills[2].name, 'Dancing')
assert.equal(users[0].skills[2].$extras.pivot_user_id, 1)
assert.equal(users[0].skills[2].$extras.pivot_skill_id, 2)

assert.equal(users[1].skills[0].name, 'Dancing')
assert.equal(users[1].skills[0].$extras.pivot_user_id, 2)
assert.equal(users[1].skills[0].$extras.pivot_skill_id, 2)
})
})

test.group('ManyToMany Query Builder | where', (group) => {
Expand Down

0 comments on commit 82c4cfb

Please sign in to comment.