Skip to content

Commit

Permalink
feat: implement sync and detach methods on many to many query builder
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Oct 13, 2019
1 parent b73b4c1 commit 0a4f4fc
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 10 deletions.
22 changes: 13 additions & 9 deletions adonis-typings/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,19 @@ declare module '@ioc:Adonis/Lucid/Model' {
checkExisting?: boolean,
): Promise<void>

// /**
// * Attach related
// */
// detach (ids: any[]): Promise<void>

// /**
// * Attach related
// */
// sync (ids: any[], detach: boolean): Promise<void>
/**
* Detach from pivot table
*/
detach (ids: (string | number)[]): Promise<void>

/**
* Sync related ids
*/
sync (
ids: (string | number)[] | { [key: string]: any },
wrapInTransaction?: boolean,
checkExisting?: boolean,
): Promise<void>
}

/**
Expand Down
118 changes: 118 additions & 0 deletions src/Orm/Relations/ManyToMany/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,82 @@ export class ManyToManyQueryBuilder
}))
}

/**
* Remove related records from the pivot table. The `inverse` flag
* will remove all except given ids
*/
private async _detach (
parent: ModelContract,
client: QueryClientContract,
ids: (string | number)[],
inverse: boolean = false,
) {
const query = client
.query()
.from(this._relation.pivotTable)
.where(
this._relation.pivotForeignKey,
this.$getRelatedValue(parent, this._relation.localKey, 'detach'),
)

if (inverse) {
query.whereNotIn(this._relation.pivotRelatedForeignKey, ids)
} else {
query.whereIn(this._relation.pivotRelatedForeignKey, ids)
}

return query.del()
}

/**
* Sync related ids inside the pivot table.
*/
private async _sync (
parent: ModelContract,
ids: (string | number)[] | { [key: string]: any },
checkExisting: boolean,
) {
const client = this._relation.model.$adapter.modelClient(parent)

/**
* Remove except given ids
*/
const detachIds = Array.isArray(ids) ? ids : Object.keys(ids)
await this._detach(parent, client, detachIds, true)

/**
* Add new ids
*/
await this._attach(parent, client, ids, checkExisting)
}

/**
* Sync related ids inside the pivot table within a transaction
*/
private async _syncInTransaction (
parent: ModelContract,
trx: TransactionClientContract,
ids: (string | number)[] | { [key: string]: any },
checkExisting: boolean,
) {
try {
/**
* Remove except given ids
*/
const detachIds = Array.isArray(ids) ? ids : Object.keys(ids)
await this._detach(parent, trx, detachIds, true)

/**
* Add new ids
*/
await this._attach(parent, trx, ids, checkExisting)
await trx.commit()
} catch (error) {
await trx.rollback()
throw error
}
}

/**
* Save related model instance with entry in the pivot table
*/
Expand Down Expand Up @@ -494,4 +570,46 @@ export class ManyToManyQueryBuilder
const client = this._relation.model.$adapter.modelClient(this._parent)
await this._attach(this._parent, client, ids, checkExisting)
}

/**
* Remove one of more related instances
*/
public async detach (ids: (string | number)[]) {
if (Array.isArray(this._parent)) {
throw new Error('Cannot save with multiple parents')
return
}

const client = this._relation.model.$adapter.modelClient(this._parent)
await this._detach(this._parent, client, ids)
}

/**
* Sync related ids
*/
public async sync (
ids: (string | number)[] | { [key: string]: any },
wrapInTransaction: boolean = true,
checkExisting: boolean = true,
) {
if (Array.isArray(this._parent)) {
throw new Error('Cannot save with multiple parents')
return
}

/**
* Wrap in transaction when wrapInTransaction is not set to false. So that
* we rollback to initial state, when one or more fails
*/
let trx: TransactionClientContract | undefined
if (wrapInTransaction) {
trx = await this.client.transaction()
}

if (trx) {
await this._syncInTransaction(this._parent, trx, ids, checkExisting)
} else {
await this._sync(this._parent, ids, checkExisting)
}
}
}
7 changes: 6 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,10 @@ export function unique (value: any[]) {
}

export function difference (main: any[], other: []) {
return [main, other].reduce((a, b) => a.filter(c => !b.includes(c)))
return [main, other].reduce((a, b) => {
return a.filter(c => {
/* tslint:disable triple-equals */
return !b.find((one) => c == one)
})
})
}
1 change: 1 addition & 0 deletions test-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export function getConfig (): ConnectionConfigContract {
filename: join(fs.basePath, 'db.sqlite'),
},
useNullAsDefault: true,
debug: true,
}
case 'mysql':
return {
Expand Down
Loading

0 comments on commit 0a4f4fc

Please sign in to comment.