From 6aeba8a4e592dca4ea4b1089fe1f768e672f9654 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Sat, 17 Oct 2015 16:26:32 +0530 Subject: [PATCH] Added support for multiple associate as per issue #4 Lucid now supports multiple associate and dissociate under single write operation --- src/Orm/Proxy/Model/index.js | 16 +- src/Orm/Proxy/Model/relation.js | 24 +- src/Orm/Proxy/Static/index.js | 78 ++- src/Orm/Proxy/Static/proxy.js | 4 +- test/implementation/storage/blog.sqlite3 | Bin 4096 -> 4096 bytes .../blueprints/model-relations-blueprint.js | 27 +- test/unit/model-relations.spec.js | 620 +++++++++++------- test/unit/storage/test.sqlite3 | Bin 12288 -> 13312 bytes 8 files changed, 465 insertions(+), 304 deletions(-) diff --git a/src/Orm/Proxy/Model/index.js b/src/Orm/Proxy/Model/index.js index 54c7bd90..8cdb7fe7 100644 --- a/src/Orm/Proxy/Model/index.js +++ b/src/Orm/Proxy/Model/index.js @@ -43,7 +43,7 @@ class Model { this.attributes = attributes ? helper.mutateRow(this, attributes) : {} /** - * here we create an isolated connection to + * here we create an isolated connection to * the database, one per model instance. */ this.connection = this.constructor.database.table(this.constructor.table) @@ -104,7 +104,7 @@ class Model { */ update () { /** - * one can only update existing model. Here we make + * one can only update existing model. Here we make * sure this model is initiated after db fetch. */ if (!helper.isFetched(this)) { @@ -124,7 +124,7 @@ class Model { */ delete () { /** - * one can only delete existing model. Here we make + * one can only delete existing model. Here we make * sure this model is initiated after db fetch. */ if (!helper.isFetched(this)) { @@ -144,7 +144,7 @@ class Model { const self = this /** - * one can only delete existing model. Here we make + * one can only delete existing model. Here we make * sure this model is initiated after db fetch. */ if (!helper.isFetched(this)) { @@ -302,7 +302,7 @@ class Model { /** * relation scopes are nested queries on relationship models, they are - * not required by model instance, but required when fetching + * not required by model instance, but required when fetching * relationships using with method. * @type {Object} */ @@ -379,7 +379,7 @@ class Model { /** * relation scopes are nested queries on relationship models, they are - * not required by model instance, but required when fetching + * not required by model instance, but required when fetching * relationships using with method. * @type {Object} */ @@ -448,7 +448,7 @@ class Model { /** * relation scopes are nested queries on relationship models, they are - * not required by model instance, but required when fetching + * not required by model instance, but required when fetching * relationships using with method. * @type {Object} */ @@ -538,7 +538,7 @@ class Model { /** * relation scopes are nested queries on relationship models, they are - * not required by model instance, but required when fetching + * not required by model instance, but required when fetching * relationships using with method. * @type {Object} */ diff --git a/src/Orm/Proxy/Model/relation.js b/src/Orm/Proxy/Model/relation.js index 3477ecb1..c451b8d7 100644 --- a/src/Orm/Proxy/Model/relation.js +++ b/src/Orm/Proxy/Model/relation.js @@ -29,7 +29,7 @@ relation.associate = function (target,model) { if(target._associationModel._activeRelation.relation !== 'belongsTo'){ throw new Error(`Unable to call associate on ${target._associationModel._activeRelation.relation}`) } - target._associationModel._associationAttributes = model.attributes + target._associationModel._associationAttributes.push({attributes:model.attributes,targetPrimaryKey:target._associationModel._activeRelation.targetPrimaryKey,relationPrimaryKey:target._associationModel._activeRelation.relationPrimaryKey}) target.new() } @@ -54,7 +54,7 @@ relation.dissociate = function (target){ if(target._associationModel._activeRelation.relation !== 'belongsTo'){ throw new Error(`Unable to call dissociate on ${target._associationModel._activeRelation.relation}`) } - target._associationModel._associationAttributes = {dissociate:true} + target._associationModel._associationAttributes.push({attributes:{dissociate:true},targetPrimaryKey:target._associationModel._activeRelation.targetPrimaryKey,relationPrimaryKey:target._associationModel._activeRelation.relationPrimaryKey}) target.new() } @@ -62,7 +62,7 @@ relation.dissociate = function (target){ * @function attach * @description this method is only belongsToMany specific and will * attach primary values from 2 models into a pivot table. - * @note this method does not touch host/relational model + * @note this method does not touch host/relational model * tables. It only make neccessary entries inside * pivot table * @param {Object} target [description] @@ -74,10 +74,10 @@ relation.attach = function (target, relationValue, extraFields) { /** * getting relationship meta data to be used while persisting - * values inside pivot table. + * values inside pivot table. */ const getPersistanceFields = relation.getFieldsForAD(target) - const pivotTable = getPersistanceFields.pivotTable + const pivotTable = getPersistanceFields.pivotTable const pivotPrimaryKey = getPersistanceFields.pivotPrimaryKey const pivotOtherKey = getPersistanceFields.pivotOtherKey const targetPrimaryKey = getPersistanceFields.targetPrimaryKey @@ -107,7 +107,7 @@ relation.attach = function (target, relationValue, extraFields) { * @function detach * @description this method is only belongsToMany specific and will * remove primary values of 2 models from pivot table. - * @note this method does not touch host/relational model + * @note this method does not touch host/relational model * tables. It only remove rows from pivot table. * @param {Object} target [description] * @param {Number} relationValue [description] @@ -117,10 +117,10 @@ relation.detach = function (target, relationValue) { /** * getting relationship meta data to be used while removing - * values inside pivot table. + * values inside pivot table. */ const getPersistanceFields = relation.getFieldsForAD(target) - const pivotTable = getPersistanceFields.pivotTable + const pivotTable = getPersistanceFields.pivotTable const pivotPrimaryKey = getPersistanceFields.pivotPrimaryKey const pivotOtherKey = getPersistanceFields.pivotOtherKey const targetPrimaryKey = getPersistanceFields.targetPrimaryKey @@ -153,7 +153,7 @@ relation.detach = function (target, relationValue) { /** * @description think of it as a real helper method to get values - * required to attach belongsToMany models values inside a + * required to attach belongsToMany models values inside a * pivot table. * @method getPersistanceFields * @param {Object} target [description] @@ -267,8 +267,8 @@ relation.resolveHasMany = function (values, relationDefination) { /** * @function resolveBelongsToMany - * @description setting up model with initial query params for - * belongsToMany. It returns query builder which can be + * @description setting up model with initial query params for + * belongsToMany. It returns query builder which can be * chained further. * @param {Object} values [description] * @param {Object} relationDefination [description] @@ -337,7 +337,7 @@ relation.resolveBelongsToMany = function (values, relationDefination) { ] /** - * we set the pivot table here. This will be used by fetch + * we set the pivot table here. This will be used by fetch * method to fetch extra pivot columns defined by user. * @type {String} */ diff --git a/src/Orm/Proxy/Static/index.js b/src/Orm/Proxy/Static/index.js index 12647849..5b4fecaf 100644 --- a/src/Orm/Proxy/Static/index.js +++ b/src/Orm/Proxy/Static/index.js @@ -22,8 +22,8 @@ class StaticProxy { Model.activeConnection = Database.table(Model.table) /** - * here we store active relation as an object which - * has useful information like + * here we store active relation as an object which + * has useful information like * relational model * foreign key * other key @@ -34,7 +34,7 @@ class StaticProxy { Model._activeRelation = {} /** - * here we store relation keys to be fetched when fetching + * here we store relation keys to be fetched when fetching * host/target model. In short these are keys sent with * `with` method * @type {Array} @@ -42,7 +42,7 @@ class StaticProxy { Model._relations = [] /** - * here we store scope methods, which should be executed on + * here we store scope methods, which should be executed on * relational query builder. We simply store these * and invoke them when running relational model * queries. @@ -80,18 +80,20 @@ class StaticProxy { Model._pivotTable = null /** - * association model to set association attributes on this is required for + * association model to set association attributes on this is required for * belongsTo method. * @type {Object} */ Model._associationModel = {} /** - * association attributes to read foreign key value from while saving a - * relation. + * association attributes to read foreign key value from while saving a + * relation. There can be multiple associationAttributes if + * associate of dissociate has been called multiple + * times. * @type {Object} */ - Model._associationAttributes = {} + Model._associationAttributes = [] /** * pivotAttributes are required to save belongsToMany relationship @@ -107,29 +109,29 @@ class StaticProxy { */ Model.create = function (values, isMutated, connection) { + const self = this /** * here we look for an active relation and if that relation is - * belongsTo then we grab associationAttributes set by + * belongsTo then we grab associationAttributes set by * associate method and grab the value of foreign * key under relation */ - if(this._activeRelation.relation === 'belongsTo' && Object.keys(this._associationAttributes).length > 0){ - const targetPrimaryKey = this._activeRelation.targetPrimaryKey - const relationPrimaryKey = this._activeRelation.relationPrimaryKey - this._foreignKey[targetPrimaryKey] = this._associationAttributes[relationPrimaryKey] + if(this._associationAttributes.length > 0){ + this._associationAttributes.forEach(function (item) { + self._foreignKey[item.targetPrimaryKey] = item.attributes[item.relationPrimaryKey] + }) } - + /** * here we set foreign key and it's value to be inserted * if create method is invoked via relational model. */ if(this._foreignKey && Object.keys(this._foreignKey).length > 0){ - const key = Object.keys(this._foreignKey)[0]; - values[key] = this._foreignKey[key] + Object.keys(this._foreignKey).forEach(function (index) { + values[index] = self._foreignKey[index] + }) } - return query.create(this, values, isMutated, connection) - } /** @@ -139,38 +141,34 @@ class StaticProxy { */ Model.update = function (values, isMutated, connection) { + const self = this + /** * here we look for an active relation and if that relation is - * belongsTo then we grab associationAttributes set by + * belongsTo then we grab associationAttributes set by * associate method and grab the value of foreign * key under relation */ - if(this._activeRelation.relation === 'belongsTo' && Object.keys(this._associationAttributes).length > 0){ - - /** - * otherwise set foriegn key value to the value of primary key - * from relational model - */ - const targetPrimaryKey = this._activeRelation.targetPrimaryKey - const relationPrimaryKey = this._activeRelation.relationPrimaryKey - - /** - * if dissociate has been called, set foreign key value to null - */ - if(this._associationAttributes.dissociate){ - this._foreignKey[targetPrimaryKey] = null - }else{ - this._foreignKey[targetPrimaryKey] = this._associationAttributes[relationPrimaryKey] - } + + if(this._associationAttributes.length > 0){ + this._associationAttributes.forEach(function (item) { + if(item.attributes.dissociate){ + self._foreignKey[item.targetPrimaryKey] = null + } + else{ + self._foreignKey[item.targetPrimaryKey] = item.attributes[item.relationPrimaryKey] + } + }) } - + /** * here we set foreign key and it's value to be inserted * if create method is invoked via relational model. */ if(this._foreignKey && Object.keys(this._foreignKey).length > 0){ - const key = Object.keys(this._foreignKey)[0]; - values[key] = this._foreignKey[key] + Object.keys(this._foreignKey).forEach(function (index) { + values[index] = self._foreignKey[index] + }) } return query.update(this, values, isMutated, connection) @@ -216,7 +214,7 @@ class StaticProxy { this._withPivot = [] this._pivotTable = null this._associationModel = {} - this._associationAttributes = {} + this._associationAttributes = [] this._pivotAttributes = {} this.activeConnection = this.database.table(this.table) diff --git a/src/Orm/Proxy/Static/proxy.js b/src/Orm/Proxy/Static/proxy.js index ece5c6ef..14d4c256 100644 --- a/src/Orm/Proxy/Static/proxy.js +++ b/src/Orm/Proxy/Static/proxy.js @@ -169,7 +169,7 @@ proxy.get = function (target, name) { } /** - * attaching belongsToMany relationships on pivot tables. + * attaching belongsToMany relationships on pivot tables. * this method read relation defination and make a * raw query using `database` property from model * constructor @@ -182,7 +182,7 @@ proxy.get = function (target, name) { /** - * removing belongsToMany relationships on pivot tables. + * removing belongsToMany relationships on pivot tables. * this method read relation defination and make a * raw query using `database` property from model * constructor diff --git a/test/implementation/storage/blog.sqlite3 b/test/implementation/storage/blog.sqlite3 index 8b9ceefadfabb5b319572e65270fdf4cef367cc9..e42b75e23524c9c0ac4764457e456ee2cd6f14cd 100644 GIT binary patch delta 67 zcmZorXi%6SCB$-^fq{Vqh?yXmamhp-BPN#P8xy9nbCnj%bzuwuk()R^unPbgu3)Yw HnDhkz35F7i delta 67 zcmZorXi%6SCB$6Bz`(!)#7q#(Xfjd9h>5vqW5P6cF8zSLXBh)P}>;gc>IWYGU Hn7j%AHV{5Pu(b9LEVFq~wDp#WjUcDHXO$N-P9YfPz#i`k{&mDL9GSSV^7YB$Ys@ zDMex?dIL-yV1XqdnFR*^0~RE9%D~crGQh&Z^J{^Eu=P9p&iDJB@9wSNT(3Pma!7m( z0Ekc^3JCzN(aY&c8XfyA`C*9r0Qb=risSF#HPN_By*Kta;5p%boD&kM6nr4G&2qz9 zsWly|wQKo{^OMDeNqM1oVR}+lSg`kHqS9$uZpp4HvhB33CCkmpin(MtmD~RW8KLBK zmdlo_$g8GXshRFcJ)a+?47*iYZkFwarO4%GvtgNzT(uU>PNOYn)hsy=Wio~Y?%vg) zKwe}rP$KU#&%G^l0*;1gLm~xF5v1__Fhzhupzs@}GXR_y4L`x;yDD++#)dC=MW2#2*s+T||vR$^+zUEEa%x2jCqqI`Rqk zW;yu)41f$xzTT{wm+1juM+U6{v_=iR$iZ|f5E6xryCON2R$$nhPv3!TC;fmRBsRJs zV!||xc$-6u@F_RIAJR`?UaoQg delta 519 zcmZvZJxc>Y5QcYlHo3dp1vNo)34UWVg_7L)dA+4Y5Ni>OUITa{Of3Uc{6{_vX@mL{AtFp@ z1dV1oVG>GC_#M_M$~(<7Pw;E`j@UtAY1kvr%sCMLtNDz%Z2m@ATw|R;jjP2{DPch? zDi_yET~TZ>fn|m-c#mb=rzr*8>O-BufF24l?zrOOBQ7Rbdw4W*oXjkg_(ZsCieON@|eBbeHd%@JFL z^&qUvhmo|&HyQ1WTC&|vh7Db-Zs;;=4}$5>+v@-kd7G?-!&>5};|0jTXRC5lq1;tY T|8<*_CG-5(cNM@FnRoL8_9AMI