2016-03-16 14:41:15 +01:00
'use strict' ;
/ * *
* Module dependencies
* /
// Public node modules.
const _ = require ( 'lodash' ) ;
2016-03-22 15:50:33 +01:00
// Node.js core
const path = require ( 'path' ) ;
2016-03-16 14:41:15 +01:00
/ *
* Set of utils for models
* /
module . exports = {
2016-07-05 14:13:35 +02:00
/ * *
* Initialize to prevent some mistakes
* /
initialize : cb => {
cb ( ) ;
} ,
2016-03-17 15:05:47 +01:00
/ * *
* Find primary key per ORM
* /
2017-01-11 17:53:40 +01:00
getPK : function ( collectionIdentity , collection , models ) {
2016-03-17 15:05:47 +01:00
if ( _ . isString ( collectionIdentity ) ) {
const ORM = this . getORM ( collectionIdentity ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
try {
const GraphQLFunctions = require ( path . resolve ( strapi . config . appPath , 'node_modules' , 'strapi-' + ORM , 'lib' , 'utils' ) ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
if ( ! _ . isUndefined ( GraphQLFunctions ) ) {
return GraphQLFunctions . getPK ( collectionIdentity , collection , models || strapi . models ) ;
}
} catch ( err ) {
return undefined ;
2016-03-17 15:05:47 +01:00
}
}
return undefined ;
} ,
/ * *
* Find primary key per ORM
* /
2017-01-11 17:53:40 +01:00
getCount : function ( collectionIdentity ) {
2016-03-17 15:05:47 +01:00
if ( _ . isString ( collectionIdentity ) ) {
const ORM = this . getORM ( collectionIdentity ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
try {
const ORMFunctions = require ( path . resolve ( strapi . config . appPath , 'node_modules' , 'strapi-' + ORM , 'lib' , 'utils' ) ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
if ( ! _ . isUndefined ( ORMFunctions ) ) {
return ORMFunctions . getCount ( collectionIdentity ) ;
}
} catch ( err ) {
return undefined ;
2016-03-17 15:05:47 +01:00
}
}
return undefined ;
} ,
2016-03-16 14:41:15 +01:00
/ * *
* Find relation nature with verbose
* /
2017-09-07 17:16:31 +02:00
getNature : ( association , key , models , currentModelName ) => {
2017-12-15 18:04:56 +01:00
try {
const types = {
current : '' ,
other : ''
} ;
2016-04-19 17:29:19 +02:00
2017-12-15 18:04:56 +01:00
if ( _ . isUndefined ( models ) ) {
models = association . plugin ? strapi . plugins [ association . plugin ] . models : strapi . models ;
}
2016-04-19 17:29:19 +02:00
2018-02-22 15:34:33 +01:00
if ( ( association . hasOwnProperty ( 'collection' ) && association . collection === '*' ) || ( association . hasOwnProperty ( 'model' ) && association . model === '*' ) ) {
if ( association . model ) {
types . current = 'morphToD' ;
} else {
types . current = 'morphTo' ;
}
const flattenedPluginsModels = Object . keys ( strapi . plugins ) . reduce ( ( acc , current ) => {
Object . keys ( strapi . plugins [ current ] . models ) . forEach ( ( model ) => {
acc [ ` ${ current } _ ${ model } ` ] = strapi . plugins [ current ] . models [ model ] ;
} ) ;
return acc ;
} , { } ) ;
const allModels = _ . merge ( { } , strapi . models , flattenedPluginsModels ) ;
// We have to find if they are a model linked to this key
_ . forIn ( allModels , model => {
_ . forIn ( model . attributes , attribute => {
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key ) {
if ( attribute . hasOwnProperty ( 'collection' ) ) {
types . other = 'collection' ;
// Break loop
return false ;
} else if ( attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'model' ;
// Break loop
return false ;
}
}
} ) ;
} ) ;
} else if ( association . hasOwnProperty ( 'via' ) && association . hasOwnProperty ( 'collection' ) ) {
2017-12-15 18:04:56 +01:00
const relatedAttribute = models [ association . collection ] . attributes [ association . via ] ;
2016-03-16 14:41:15 +01:00
2018-02-27 11:52:18 +01:00
if ( ! relatedAttribute ) {
throw new Error ( ` The attribute \` ${ association . via } \` is missing in the model ${ _ . upperFirst ( association . collection ) } ${ association . plugin ? '(plugin - ' + association . plugin + ')' : '' } ` ) ;
}
2017-12-15 18:04:56 +01:00
types . current = 'collection' ;
2016-03-16 14:41:15 +01:00
2018-02-22 15:34:33 +01:00
if ( relatedAttribute . hasOwnProperty ( 'collection' ) && relatedAttribute . collection !== '*' && relatedAttribute . hasOwnProperty ( 'via' ) ) {
2017-12-15 18:04:56 +01:00
types . other = 'collection' ;
2018-02-22 15:34:33 +01:00
} else if ( relatedAttribute . hasOwnProperty ( 'collection' ) && relatedAttribute . collection !== '*' && ! relatedAttribute . hasOwnProperty ( 'via' ) ) {
2017-12-15 18:04:56 +01:00
types . other = 'collectionD' ;
2018-02-22 15:34:33 +01:00
} else if ( relatedAttribute . hasOwnProperty ( 'model' ) && relatedAttribute . model !== '*' ) {
2017-12-15 18:04:56 +01:00
types . other = 'model' ;
2018-02-22 15:34:33 +01:00
} else if ( relatedAttribute . hasOwnProperty ( 'collection' ) || relatedAttribute . hasOwnProperty ( 'model' ) ) {
2018-02-09 10:43:09 +01:00
types . other = 'morphTo' ;
2017-12-15 18:04:56 +01:00
}
} else if ( association . hasOwnProperty ( 'via' ) && association . hasOwnProperty ( 'model' ) ) {
types . current = 'modelD' ;
// We have to find if they are a model linked to this key
2018-04-30 17:14:13 +02:00
const model = models [ association . model ]
const attribute = model . attributes [ association . via ] ;
2016-08-08 11:12:09 +02:00
2018-04-30 17:14:13 +02:00
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key && attribute . hasOwnProperty ( 'collection' ) && attribute . collection !== '*' ) {
types . other = 'collection' ;
} else if ( attribute . hasOwnProperty ( 'model' ) && attribute . model !== '*' ) {
types . other = 'model' ;
} else if ( attribute . hasOwnProperty ( 'collection' ) || attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'morphTo' ;
}
2017-12-15 18:04:56 +01:00
} else if ( association . hasOwnProperty ( 'model' ) ) {
types . current = 'model' ;
// We have to find if they are a model linked to this key
_ . forIn ( models , model => {
_ . forIn ( model . attributes , attribute => {
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key ) {
if ( attribute . hasOwnProperty ( 'collection' ) ) {
types . other = 'collection' ;
// Break loop
return false ;
} else if ( attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'modelD' ;
// Break loop
return false ;
}
2016-03-16 14:41:15 +01:00
}
2017-12-15 18:04:56 +01:00
} ) ;
2016-03-16 14:41:15 +01:00
} ) ;
2017-12-15 18:04:56 +01:00
} else if ( association . hasOwnProperty ( 'collection' ) ) {
types . current = 'collectionD' ;
// We have to find if they are a model linked to this key
_ . forIn ( models , model => {
_ . forIn ( model . attributes , attribute => {
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key ) {
if ( attribute . hasOwnProperty ( 'collection' ) ) {
types . other = 'collection' ;
// Break loop
return false ;
} else if ( attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'modelD' ;
2018-02-12 18:54:34 +01:00
// Break loop
return false ;
}
}
} ) ;
} ) ;
2017-12-15 18:04:56 +01:00
}
2016-03-16 14:41:15 +01:00
2018-02-12 18:54:34 +01:00
if ( types . current === 'collection' && types . other === 'morphTo' ) {
2018-02-21 17:33:30 +01:00
return {
nature : 'manyToManyMorph' ,
verbose : 'morphMany'
} ;
} else if ( types . current === 'collection' && types . other === 'morphToD' ) {
2018-02-09 10:43:09 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'manyToOneMorph' ,
2018-02-20 19:59:05 +01:00
verbose : 'morphMany'
2018-02-09 10:43:09 +01:00
} ;
2018-02-21 17:33:30 +01:00
} else if ( types . current === 'modelD' && types . other === 'morphTo' ) {
return {
nature : 'oneToManyMorph' ,
verbose : 'morphOne'
} ;
} else if ( types . current === 'modelD' && types . other === 'morphToD' ) {
2018-02-09 10:43:09 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'oneToOneMorph' ,
2018-02-20 19:59:05 +01:00
verbose : 'morphOne'
2018-02-09 10:43:09 +01:00
} ;
2018-02-21 17:33:30 +01:00
} else if ( types . current === 'morphToD' && types . other === 'collection' ) {
2018-02-09 10:43:09 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'oneMorphToMany' ,
2018-02-12 18:54:34 +01:00
verbose : 'belongsToMorph'
} ;
2018-02-21 17:33:30 +01:00
} else if ( types . current === 'morphToD' && types . other === 'model' ) {
2018-02-12 18:54:34 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'oneMorphToOne' ,
2018-02-21 17:33:30 +01:00
verbose : 'belongsToMorph'
} ;
2018-02-26 11:12:49 +01:00
} else if ( types . current === 'morphTo' && ( types . other === 'model' || association . hasOwnProperty ( 'model' ) ) ) {
2018-02-21 17:33:30 +01:00
return {
nature : 'manyMorphToOne' ,
verbose : 'belongsToManyMorph'
} ;
2018-02-26 11:12:49 +01:00
} else if ( types . current === 'morphTo' && ( types . other === 'collection' || association . hasOwnProperty ( 'collection' ) ) ) {
2018-02-21 17:33:30 +01:00
return {
nature : 'manyMorphToMany' ,
2018-02-12 18:54:34 +01:00
verbose : 'belongsToManyMorph'
2018-02-09 10:43:09 +01:00
} ;
} else if ( types . current === 'modelD' && types . other === 'model' ) {
2017-12-15 18:04:56 +01:00
return {
nature : 'oneToOne' ,
verbose : 'belongsTo'
} ;
} else if ( types . current === 'model' && types . other === 'modelD' ) {
return {
nature : 'oneToOne' ,
verbose : 'hasOne'
} ;
} else if ( ( types . current === 'model' || types . current === 'modelD' ) && types . other === 'collection' ) {
return {
nature : 'manyToOne' ,
verbose : 'belongsTo'
} ;
} else if ( types . current === 'modelD' && types . other === 'collection' ) {
return {
nature : 'oneToMany' ,
verbose : 'hasMany'
} ;
} else if ( types . current === 'collection' && types . other === 'model' ) {
return {
nature : 'oneToMany' ,
verbose : 'hasMany'
} ;
} else if ( types . current === 'collection' && types . other === 'collection' ) {
return {
nature : 'manyToMany' ,
verbose : 'belongsToMany'
} ;
} else if ( types . current === 'collectionD' && types . other === 'collection' || types . current === 'collection' && types . other === 'collectionD' ) {
return {
nature : 'manyToMany' ,
verbose : 'belongsToMany'
} ;
} else if ( types . current === 'collectionD' && types . other === '' ) {
return {
nature : 'manyWay' ,
verbose : 'belongsToMany'
} ;
} else if ( types . current === 'model' && types . other === '' ) {
return {
nature : 'oneWay' ,
verbose : 'belongsTo'
} ;
}
2016-03-16 14:41:15 +01:00
2017-12-15 18:04:56 +01:00
return undefined ;
} catch ( e ) {
strapi . log . error ( ` Something went wrong in the model \` ${ _ . upperFirst ( currentModelName ) } \` with the attribute \` ${ key } \` ` ) ;
2018-02-21 17:33:30 +01:00
strapi . log . error ( e ) ;
2017-12-15 18:04:56 +01:00
strapi . stop ( ) ;
}
2016-03-16 14:41:15 +01:00
} ,
/ * *
* Return ORM used for this collection .
* /
2016-11-07 16:31:34 +01:00
getORM : collectionIdentity => {
2016-03-16 14:41:15 +01:00
return _ . get ( strapi . models , collectionIdentity . toLowerCase ( ) + '.orm' ) ;
} ,
/ * *
* Define associations key to models
* /
2017-01-11 17:53:40 +01:00
defineAssociations : function ( model , definition , association , key ) {
2017-12-16 18:26:04 +01:00
try {
// Initialize associations object
if ( definition . associations === undefined ) {
definition . associations = [ ] ;
}
2016-03-16 14:41:15 +01:00
2017-12-16 18:26:04 +01:00
// Exclude non-relational attribute
2018-02-22 15:34:33 +01:00
if ( ! association . hasOwnProperty ( 'collection' ) && ! association . hasOwnProperty ( 'model' ) ) {
2017-12-16 18:26:04 +01:00
return undefined ;
}
2016-03-16 14:41:15 +01:00
2017-12-16 18:26:04 +01:00
// Get relation nature
2018-02-22 15:34:33 +01:00
let details ;
const globalName = association . model || association . collection || '' ;
2017-12-16 18:26:04 +01:00
const infos = this . getNature ( association , key , undefined , model . toLowerCase ( ) ) ;
2018-02-22 15:34:33 +01:00
if ( globalName !== '*' ) {
2018-02-27 11:52:18 +01:00
details = association . plugin ?
_ . get ( strapi . plugins , ` ${ association . plugin } .models. ${ globalName } .attributes. ${ association . via } ` , { } ) :
_ . get ( strapi . models , ` ${ globalName } .attributes. ${ association . via } ` , { } ) ;
2018-02-22 15:34:33 +01:00
}
2017-12-16 18:26:04 +01:00
// Build associations object
2018-02-22 15:34:33 +01:00
if ( association . hasOwnProperty ( 'collection' ) && association . collection !== '*' ) {
2017-12-16 18:26:04 +01:00
definition . associations . push ( {
alias : key ,
type : 'collection' ,
collection : association . collection ,
via : association . via || undefined ,
nature : infos . nature ,
autoPopulate : _ . get ( association , 'autoPopulate' , true ) ,
dominant : details . dominant !== true ,
plugin : association . plugin || undefined ,
2018-02-22 16:08:11 +01:00
filter : details . filter ,
2017-12-16 18:26:04 +01:00
} ) ;
2018-02-22 15:34:33 +01:00
} else if ( association . hasOwnProperty ( 'model' ) && association . model !== '*' ) {
2017-12-16 18:26:04 +01:00
definition . associations . push ( {
alias : key ,
type : 'model' ,
model : association . model ,
via : association . via || undefined ,
nature : infos . nature ,
autoPopulate : _ . get ( association , 'autoPopulate' , true ) ,
dominant : details . dominant !== true ,
plugin : association . plugin || undefined ,
2018-02-22 16:08:11 +01:00
filter : details . filter ,
2018-02-12 18:54:34 +01:00
} ) ;
2018-02-22 15:34:33 +01:00
} else if ( association . hasOwnProperty ( 'collection' ) || association . hasOwnProperty ( 'model' ) ) {
2018-02-20 19:59:05 +01:00
const pluginsModels = Object . keys ( strapi . plugins ) . reduce ( ( acc , current ) => {
Object . keys ( strapi . plugins [ current ] . models ) . forEach ( ( entity ) => {
Object . keys ( strapi . plugins [ current ] . models [ entity ] . attributes ) . forEach ( ( attribute ) => {
const attr = strapi . plugins [ current ] . models [ entity ] . attributes [ attribute ] ;
2018-02-26 11:12:49 +01:00
2018-02-20 19:59:05 +01:00
if (
( attr . collection || attr . model || '' ) . toLowerCase ( ) === model . toLowerCase ( ) &&
strapi . plugins [ current ] . models [ entity ] . globalId !== definition . globalId
) {
acc . push ( strapi . plugins [ current ] . models [ entity ] . globalId ) ;
}
} ) ;
} ) ;
return acc ;
} , [ ] ) ;
const appModels = Object . keys ( strapi . models ) . reduce ( ( acc , entity ) => {
Object . keys ( strapi . models [ entity ] . attributes ) . forEach ( ( attribute ) => {
const attr = strapi . models [ entity ] . attributes [ attribute ] ;
if (
( attr . collection || attr . model || '' ) . toLowerCase ( ) === model . toLowerCase ( ) &&
strapi . models [ entity ] . globalId !== definition . globalId
) {
acc . push ( strapi . models [ entity ] . globalId ) ;
}
} ) ;
return acc ;
} , [ ] ) ;
const models = _ . uniq ( appModels . concat ( pluginsModels ) ) ;
2018-02-12 18:54:34 +01:00
definition . associations . push ( {
alias : key ,
2018-02-22 15:34:33 +01:00
type : association . model ? 'model' : 'collection' ,
2018-02-20 19:59:05 +01:00
related : models ,
2018-02-12 18:54:34 +01:00
nature : infos . nature ,
autoPopulate : _ . get ( association , 'autoPopulate' , true ) ,
2018-02-22 15:34:33 +01:00
filter : association . filter ,
2017-12-16 18:26:04 +01:00
} ) ;
}
} catch ( e ) {
strapi . log . error ( ` Something went wrong in the model \` ${ _ . upperFirst ( model ) } \` with the attribute \` ${ key } \` ` ) ;
2018-02-21 17:33:30 +01:00
strapi . log . error ( e ) ;
2017-12-16 18:26:04 +01:00
strapi . stop ( ) ;
2016-03-16 14:41:15 +01:00
}
2016-08-08 11:12:09 +02:00
} ,
2016-11-07 16:31:34 +01:00
getVia : ( attribute , association ) => {
2016-09-28 11:42:26 +02:00
return _ . findKey ( strapi . models [ association . model || association . collection ] . attributes , { via : attribute } ) ;
2017-09-12 17:58:31 +02:00
} ,
2017-09-13 10:30:37 +02:00
convertParams : ( entity , params ) => {
if ( ! entity ) {
2017-10-10 15:15:17 +02:00
throw new Error ( 'You can\'t call the convert params method without passing the model\'s name as a first argument.' ) ;
2017-09-12 17:58:31 +02:00
}
2017-09-13 10:30:37 +02:00
const model = entity . toLowerCase ( ) ;
2017-11-20 14:35:24 +01:00
const models = _ . assign ( _ . clone ( strapi . models ) , Object . keys ( strapi . plugins ) . reduce ( ( acc , current ) => {
2017-11-17 12:05:03 +01:00
_ . assign ( acc , _ . get ( strapi . plugins [ current ] , [ 'models' ] , { } ) ) ;
return acc ;
} , { } ) ) ;
2017-09-13 10:30:37 +02:00
2017-11-17 12:05:03 +01:00
if ( ! models . hasOwnProperty ( model ) ) {
2017-09-13 10:30:37 +02:00
return this . log . error ( ` The model ${ model } can't be found. ` ) ;
}
2017-11-17 12:05:03 +01:00
const connector = models [ model ] . orm ;
2017-09-13 10:30:37 +02:00
if ( ! connector ) {
2017-12-07 14:35:35 +01:00
throw new Error ( ` Impossible to determine the ORM used for the model ${ model } . ` ) ;
2017-09-13 10:30:37 +02:00
}
const convertor = strapi . hook [ connector ] . load ( ) . getQueryParams ;
2017-09-12 17:58:31 +02:00
const convertParams = {
where : { } ,
2017-09-13 12:18:54 +02:00
sort : '' ,
2017-09-12 17:58:31 +02:00
start : 0 ,
limit : 100
} ;
_ . forEach ( params , ( value , key ) => {
let result ;
if ( _ . includes ( [ '_start' , '_limit' ] , key ) ) {
result = convertor ( value , key ) ;
} else if ( key === '_sort' ) {
const [ attr , order ] = value . split ( ':' ) ;
result = convertor ( order , key , attr ) ;
} else {
const suffix = key . split ( '_' ) ;
let type ;
2018-04-10 18:54:01 +02:00
if ( _ . includes ( [ 'ne' , 'lt' , 'gt' , 'lte' , 'gte' , 'contains' , 'containss' ] , _ . last ( suffix ) ) ) {
2017-09-12 17:58:31 +02:00
type = ` _ ${ _ . last ( suffix ) } ` ;
key = _ . dropRight ( suffix ) . join ( '_' ) ;
} else {
type = '=' ;
}
result = convertor ( value , type , key ) ;
}
_ . set ( convertParams , result . key , result . value ) ;
} ) ;
return convertParams ;
2016-03-16 14:41:15 +01:00
}
} ;