Skip to content

Commit

Permalink
feat(relations): Added support for multiple relations using lucid orm.
Browse files Browse the repository at this point in the history
Lucid now will support hasOne,hasMany,belongsTo,belongToMany relationships for fetching and
persisting data. Fetching relations works the same way you want them to work
  • Loading branch information
thetutlage committed Oct 3, 2015
1 parent 6ab0ab2 commit 91d046a
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 17 deletions.
21 changes: 16 additions & 5 deletions src/Orm/Proxy/Model/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,30 @@ class Model {
* @public
*/
create (values) {
let isMutated = !values
values = values || this.attributes

/**
* here we consume the create method on model
* constructor but makes sure to set it back
* as primary key on model instance.
*/
return new Promise((resolve,reject) => {

/**
* throw an error if trying to save multiple
* rows via model instance
*/
if(values && _.isArray(values)){
return reject(new Error('cannot persist model with multiple rows'))
}

let isMutated = !values
values = values || this.attributes

this.constructor
.create(values, isMutated, this.connection)
.then ((response) => {
if(response[0]){
this.attributes = helpers.mutateRow(this,values)
this.attributes[this.constructor.primaryKey] = response[0]
}
resolve(response)
Expand All @@ -92,12 +103,12 @@ class Model {
* @return {Promise}
* @public
*/
update (values) {
update () {
if (!helpers.isFetched(this)) {
throw new Error(`You cannot update a fresh model instance , trying fetching one using find method`)
}
let isMutated = !values
values = values || this.attributes
const values = this.attributes
const isMutated = true
return this.constructor.update(values, isMutated, this.connection)
}

Expand Down
8 changes: 7 additions & 1 deletion src/Orm/Proxy/Static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ class StaticProxy {
*/
Model._associationAttributes = {}


/**
* pivotAttributes are required to save belongsToMany relationship
* under a pivot table
* @type {Object}
*/
Model._pivotAttributes = {}

/**
Expand Down Expand Up @@ -123,7 +127,9 @@ class StaticProxy {
const key = Object.keys(this._foreignKey)[0];
values[key] = this._foreignKey[key]
}

return addons.create(this, values, isMutated, connection)

}

/**
Expand Down
95 changes: 91 additions & 4 deletions src/Orm/Proxy/Static/mapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,16 @@ mapper.get = function (target, name) {
}
}

/**
* associating foreign key via belongsTo
* relationship
*/
if(name === 'associate'){

/**
* @param {Object} model Model to associate
* @return {void}
*/
return function (model) {

/**
Expand All @@ -165,8 +174,12 @@ mapper.get = function (target, name) {
}


/**
* dissociating foreign key via belongsTo
* relationship
*/
if(name === 'dissociate'){
return function (model) {
return function () {

/**
* making sure that dissociate is called on belongsTo relation only.
Expand All @@ -182,8 +195,15 @@ mapper.get = function (target, name) {
}
}

/**
* attaching belongsToMany relationships on pivot tables.
* this method read relation defination and make a
* raw query using `database` property from model
* constructor
*/
if(name === 'attach'){
return function (relationValue){

return function (relationValue, extraFields){

return new Promise(function (resolve, reject) {
/**
Expand All @@ -196,25 +216,92 @@ mapper.get = function (target, name) {
return reject(`Unable to call attach on ${target._associationModel._activeRelation.relation}`)
}

/**
* properly reading pivot relation keys from association model relation.
*/
const pivotTable = target._associationModel._activeRelation.pivotTable
const pivotPrimaryKey = target._associationModel._activeRelation.pivotPrimaryKey
const pivotOtherKey = target._associationModel._activeRelation.pivotOtherKey
const targetPrimaryKey = target._associationModel._activeRelation.targetPrimaryKey

const pivotValues = {}
/**
* values object to be inserted inside pivot table
* @type {Object}
*/
const pivotValues = extraFields || {}
pivotValues[pivotPrimaryKey] = target._pivotAttributes[targetPrimaryKey]
pivotValues[pivotOtherKey] = relationValue

/**
* here we need to clean the association model also , as
* no other method will be called on association model
* after attach.
*/
target._associationModel.new()

target.new()

return target.database.table(pivotTable).insert(pivotValues).then(resolve).catch(reject)
/**
* raw query to insert relationship inside pivot table
*/
target.database.table(pivotTable).insert(pivotValues).then(resolve).catch(reject)
})

}
}


if(name === 'detach'){

return function (relationValue){

return new Promise(function (resolve, reject) {
/**
* making sure that attach is called on belongsToMany relation only.
*/
if(!target._associationModel._activeRelation || !target._pivotAttributes){
return reject('unable to call attach , make sure to call relationship method before dissociate')
}
if(target._associationModel._activeRelation.relation !== 'belongsToMany'){
return reject(`Unable to call attach on ${target._associationModel._activeRelation.relation}`)
}

/**
* properly reading pivot relation keys from association model relation.
*/
const pivotTable = target._associationModel._activeRelation.pivotTable
const pivotPrimaryKey = target._associationModel._activeRelation.pivotPrimaryKey
const pivotOtherKey = target._associationModel._activeRelation.pivotOtherKey
const targetPrimaryKey = target._associationModel._activeRelation.targetPrimaryKey

/**
* values object to be inserted inside pivot table
* @type {Object}
*/
const whereClause = {}
whereClause[pivotPrimaryKey] = target._pivotAttributes[targetPrimaryKey]
if(relationValue){
whereClause[pivotOtherKey] = relationValue
}

/**
* here we need to clean the association model also , as
* no other method will be called on association model
* after attach.
*/
target._associationModel.new()

target.new()

/**
* raw query to insert relationship inside pivot table
*/
target.database.table(pivotTable).where(whereClause).delete().then(resolve).catch(reject)
})

}
}

/**
* check to see if method is one of the scoped
* methods or not, if method falls into a
Expand Down
Binary file modified test/implementation/storage/blog.sqlite3
Binary file not shown.
Loading

0 comments on commit 91d046a

Please sign in to comment.