mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 01:47:13 +00:00 
			
		
		
		
	[WIP] Polymorphic associations with Bookshelf
This commit is contained in:
		
							parent
							
								
									128a3a67e6
								
							
						
					
					
						commit
						dc75b51d6f
					
				| @ -1,16 +0,0 @@ | |||||||
| root = true |  | ||||||
| 
 |  | ||||||
| [*] |  | ||||||
| indent_style = space |  | ||||||
| indent_size = 2 |  | ||||||
| end_of_line = lf |  | ||||||
| charset = utf-8 |  | ||||||
| trim_trailing_whitespace = true |  | ||||||
| insert_final_newline = true |  | ||||||
| 
 |  | ||||||
| [{package.json,*.yml}] |  | ||||||
| indent_style = space |  | ||||||
| indent_size = 2 |  | ||||||
| 
 |  | ||||||
| [*.md] |  | ||||||
| trim_trailing_whitespace = false |  | ||||||
| @ -133,6 +133,30 @@ module.exports = function(strapi) { | |||||||
|                       : key; |                       : key; | ||||||
|                   }); |                   }); | ||||||
| 
 | 
 | ||||||
|  |                 if (definition.globalId === 'Upload') { | ||||||
|  |                   loadedModel.serialize = function(options) { | ||||||
|  |                     const attrs = _.clone(this.attributes); | ||||||
|  | 
 | ||||||
|  |                     if (options && options.shallow) { | ||||||
|  |                       return attrs; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     const relations = this.relations; | ||||||
|  | 
 | ||||||
|  |                     for (let key in relations) { | ||||||
|  |                       const relation = relations[key]; | ||||||
|  | 
 | ||||||
|  |                       attrs[key] = relation.toJSON ? relation.toJSON(options) : relation; | ||||||
|  | 
 | ||||||
|  |                       if (key === 'related') { | ||||||
|  |                         attrs[key] = attrs[key].map(rel => rel['related']); | ||||||
|  |                       } | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return attrs; | ||||||
|  |                   } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 // Initialize lifecycle callbacks.
 |                 // Initialize lifecycle callbacks.
 | ||||||
|                 loadedModel.initialize = function() { |                 loadedModel.initialize = function() { | ||||||
|                   const lifecycle = { |                   const lifecycle = { | ||||||
| @ -150,6 +174,22 @@ module.exports = function(strapi) { | |||||||
|                     saved: 'afterSave' |                     saved: 'afterSave' | ||||||
|                   }; |                   }; | ||||||
| 
 | 
 | ||||||
|  |                   if (definition.globalId === 'Upload') { | ||||||
|  |                     this.on('fetching fetching:collection', (instance, attrs, options) => { | ||||||
|  |                       if (_.isArray(options.withRelated)) { | ||||||
|  |                         options.withRelated = options.withRelated.map(path => { | ||||||
|  |                           if (_.isString(path) && path === 'related') { | ||||||
|  |                             return 'related.related'; | ||||||
|  |                           } | ||||||
|  | 
 | ||||||
|  |                           return path; | ||||||
|  |                         }) | ||||||
|  |                       } | ||||||
|  | 
 | ||||||
|  |                       return Promise.resolve(); | ||||||
|  |                     }); | ||||||
|  |                   } | ||||||
|  | 
 | ||||||
|                   _.forEach(lifecycle, (fn, key) => { |                   _.forEach(lifecycle, (fn, key) => { | ||||||
|                     if (_.isFunction(target[model.toLowerCase()][fn])) { |                     if (_.isFunction(target[model.toLowerCase()][fn])) { | ||||||
|                       this.on(key, target[model.toLowerCase()][fn]); |                       this.on(key, target[model.toLowerCase()][fn]); | ||||||
| @ -369,6 +409,69 @@ module.exports = function(strapi) { | |||||||
|                   }; |                   }; | ||||||
|                   break; |                   break; | ||||||
|                 } |                 } | ||||||
|  |                 case 'morphOne': { | ||||||
|  |                   loadedModel[name] =  function() { | ||||||
|  |                     return this.morphOne(GLOBALS[globalId], details.via); | ||||||
|  |                   } | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |                 case 'morphMany': { | ||||||
|  |                   loadedModel[name] =  function() { | ||||||
|  |                     return this.morphMany(GLOBALS[globalId], details.via); | ||||||
|  |                   } | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|  |                 case 'belongsToMorph': | ||||||
|  |                 case 'belongsToManyMorph': { | ||||||
|  |                   const association = definition.associations | ||||||
|  |                     .find(association => association.alias === name); | ||||||
|  | 
 | ||||||
|  |                   // console.log("coucou");
 | ||||||
|  |                   // console.log(association.related.map(id => GLOBALS[id]));
 | ||||||
|  | 
 | ||||||
|  |                   const morphValues = association.related.map(id => { | ||||||
|  |                     let models = Object.values(strapi.models).filter(model => model.globalId === id); | ||||||
|  | 
 | ||||||
|  |                     if (models.length === 0) { | ||||||
|  |                       models = Object.keys(strapi.plugins).reduce((acc, current) => { | ||||||
|  |                         const models = Object.values(strapi.plugins[current].models).filter(model => model.globalId === id); | ||||||
|  | 
 | ||||||
|  |                         if (acc.length === 0 && models.length > 0) { | ||||||
|  |                           acc = models; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         return acc; | ||||||
|  |                       }, []); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     if (models.length === 0) { | ||||||
|  |                       strapi.log.error('Impossible to register the `' + model + '` model.'); | ||||||
|  |                       strapi.log.error('The collection name cannot be found for the morphTo method.'); | ||||||
|  |                       strapi.stop(); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     return models[0].collectionName; | ||||||
|  |                   }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                   // Define new model.
 | ||||||
|  |                   const options = { | ||||||
|  |                     tableName: `${loadedModel.tableName}_morph`, | ||||||
|  |                     related: function () { | ||||||
|  |                       return this.morphTo(name, ...association.related.map((id, index) => [GLOBALS[id], morphValues[index]])); | ||||||
|  |                     } | ||||||
|  |                   }; | ||||||
|  | 
 | ||||||
|  |                   const MorphModel = ORM.Model.extend(options); | ||||||
|  | 
 | ||||||
|  |                   loadedModel[name] = function () { | ||||||
|  |                     return this.hasMany( | ||||||
|  |                       MorphModel, | ||||||
|  |                       'upload_id' | ||||||
|  |                     ); | ||||||
|  |                   }; | ||||||
|  |                   break; | ||||||
|  |                 } | ||||||
|                 default: { |                 default: { | ||||||
|                   break; |                   break; | ||||||
|                 } |                 } | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
|   }, |   }, | ||||||
|   "main": "./lib", |   "main": "./lib", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "bookshelf": "^0.10.3", |     "bookshelf": "^0.12.1", | ||||||
|     "lodash": "^4.17.4", |     "lodash": "^4.17.4", | ||||||
|     "pluralize": "^6.0.0", |     "pluralize": "^6.0.0", | ||||||
|     "strapi-knex": "3.0.0-alpha.9.3", |     "strapi-knex": "3.0.0-alpha.9.3", | ||||||
|  | |||||||
| @ -296,20 +296,6 @@ module.exports = function (strapi) { | |||||||
|                         justOne: true |                         justOne: true | ||||||
|                       }; |                       }; | ||||||
| 
 | 
 | ||||||
|                       // Set this info to be able to see if this field is a real database's field.
 |  | ||||||
|                       details.isVirtual = true; |  | ||||||
|                     } else if (FK.nature === 'oneToMorph') { |  | ||||||
|                       const key = details.plugin ? |  | ||||||
|                         strapi.plugins[details.plugin].models[details.model].attributes[details.via].key: |  | ||||||
|                         strapi.models[details.model].attributes[details.via].key; |  | ||||||
| 
 |  | ||||||
|                       definition.loadedModel[name] = { |  | ||||||
|                         type: 'virtual', |  | ||||||
|                         ref, |  | ||||||
|                         via: `${FK.via}.${key}`, |  | ||||||
|                         justOne: true |  | ||||||
|                       }; |  | ||||||
| 
 |  | ||||||
|                       // Set this info to be able to see if this field is a real database's field.
 |                       // Set this info to be able to see if this field is a real database's field.
 | ||||||
|                       details.isVirtual = true; |                       details.isVirtual = true; | ||||||
|                     } else { |                     } else { | ||||||
| @ -326,7 +312,7 @@ module.exports = function (strapi) { | |||||||
|                     const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId; |                     const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId; | ||||||
| 
 | 
 | ||||||
|                     // One-side of the relationship has to be a virtual field to be bidirectional.
 |                     // One-side of the relationship has to be a virtual field to be bidirectional.
 | ||||||
|                     if ((FK && _.isUndefined(FK.via)) || details.dominant !== true && FK.nature !== 'manyToMorph') { |                     if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) { | ||||||
|                       definition.loadedModel[name] = { |                       definition.loadedModel[name] = { | ||||||
|                         type: 'virtual', |                         type: 'virtual', | ||||||
|                         ref, |                         ref, | ||||||
| @ -335,7 +321,35 @@ module.exports = function (strapi) { | |||||||
| 
 | 
 | ||||||
|                       // Set this info to be able to see if this field is a real database's field.
 |                       // Set this info to be able to see if this field is a real database's field.
 | ||||||
|                       details.isVirtual = true; |                       details.isVirtual = true; | ||||||
|                     } else if (FK.nature === 'manyToMorph') { |                     } else { | ||||||
|  |                       definition.loadedModel[name] = [{ | ||||||
|  |                         type: instance.Schema.Types.ObjectId, | ||||||
|  |                         ref | ||||||
|  |                       }]; | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  |                   } | ||||||
|  |                   case 'morphOne': { | ||||||
|  |                     const FK = _.find(definition.associations, {alias: name}); | ||||||
|  |                     const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId; | ||||||
|  |                     const key = details.plugin ? | ||||||
|  |                       strapi.plugins[details.plugin].models[details.model].attributes[details.via].key: | ||||||
|  |                       strapi.models[details.model].attributes[details.via].key; | ||||||
|  | 
 | ||||||
|  |                     definition.loadedModel[name] = { | ||||||
|  |                       type: 'virtual', | ||||||
|  |                       ref, | ||||||
|  |                       via: `${FK.via}.${key}`, | ||||||
|  |                       justOne: true | ||||||
|  |                     }; | ||||||
|  | 
 | ||||||
|  |                     // Set this info to be able to see if this field is a real database's field.
 | ||||||
|  |                     details.isVirtual = true; | ||||||
|  |                     break; | ||||||
|  |                   } | ||||||
|  |                   case 'morphMany': { | ||||||
|  |                     const FK = _.find(definition.associations, {alias: name}); | ||||||
|  |                     const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId; | ||||||
|                     const key = details.plugin ? |                     const key = details.plugin ? | ||||||
|                       strapi.plugins[details.plugin].models[details.collection].attributes[details.via].key: |                       strapi.plugins[details.plugin].models[details.collection].attributes[details.via].key: | ||||||
|                       strapi.models[details.collection].attributes[details.via].key; |                       strapi.models[details.collection].attributes[details.via].key; | ||||||
| @ -348,12 +362,6 @@ module.exports = function (strapi) { | |||||||
| 
 | 
 | ||||||
|                     // Set this info to be able to see if this field is a real database's field.
 |                     // Set this info to be able to see if this field is a real database's field.
 | ||||||
|                     details.isVirtual = true; |                     details.isVirtual = true; | ||||||
|                     } else { |  | ||||||
|                       definition.loadedModel[name] = [{ |  | ||||||
|                         type: instance.Schema.Types.ObjectId, |  | ||||||
|                         ref |  | ||||||
|                       }]; |  | ||||||
|                     } |  | ||||||
|                     break; |                     break; | ||||||
|                   } |                   } | ||||||
|                   case 'belongsToMorph': { |                   case 'belongsToMorph': { | ||||||
|  | |||||||
| @ -38,6 +38,14 @@ | |||||||
|       "via": "users", |       "via": "users", | ||||||
|       "plugin": "users-permissions", |       "plugin": "users-permissions", | ||||||
|       "configurable": false |       "configurable": false | ||||||
|  |     }, | ||||||
|  |     "avatar": { | ||||||
|  |       "model": "upload", | ||||||
|  |       "via": "related" | ||||||
|  |     }, | ||||||
|  |     "photos": { | ||||||
|  |       "collection": "upload", | ||||||
|  |       "via": "related" | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -199,12 +199,12 @@ module.exports = { | |||||||
|       if (types.current === 'collection' && types.other === 'morphTo') { |       if (types.current === 'collection' && types.other === 'morphTo') { | ||||||
|         return { |         return { | ||||||
|           nature: 'manyToMorph', |           nature: 'manyToMorph', | ||||||
|           verbose: 'belongsToMany' |           verbose: 'morphMany' | ||||||
|         }; |         }; | ||||||
|       } else if (types.current === 'modelD' && types.other === 'morphTo') { |       } else if (types.current === 'modelD' && types.other === 'morphTo') { | ||||||
|         return { |         return { | ||||||
|           nature: 'oneToMorph', |           nature: 'oneToMorph', | ||||||
|           verbose: 'belongsTo' |           verbose: 'morphOne' | ||||||
|         }; |         }; | ||||||
|       } else if (types.current === 'morphTo' && types.other === 'collection') { |       } else if (types.current === 'morphTo' && types.other === 'collection') { | ||||||
|         return { |         return { | ||||||
| @ -324,9 +324,43 @@ module.exports = { | |||||||
|           where: details.where, |           where: details.where, | ||||||
|         }); |         }); | ||||||
|       } else if (association.hasOwnProperty('key')) { |       } else if (association.hasOwnProperty('key')) { | ||||||
|  |         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]; | ||||||
|  |               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)); | ||||||
|  | 
 | ||||||
|         definition.associations.push({ |         definition.associations.push({ | ||||||
|           alias: key, |           alias: key, | ||||||
|           type: 'collection', |           type: 'collection', | ||||||
|  |           related: models, | ||||||
|           nature: infos.nature, |           nature: infos.nature, | ||||||
|           autoPopulate: _.get(association, 'autoPopulate', true), |           autoPopulate: _.get(association, 'autoPopulate', true), | ||||||
|           key: association.key, |           key: association.key, | ||||||
|  | |||||||
| @ -154,8 +154,8 @@ module.exports = { | |||||||
| 
 | 
 | ||||||
| CREATE TABLE ${quote}${Model.tableName || Model.collectionName}${quote} ( | CREATE TABLE ${quote}${Model.tableName || Model.collectionName}${quote} ( | ||||||
|   id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY, |   id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY, | ||||||
|   key text, |   ${quote}key${quote} text, | ||||||
|   value text, |   ${quote}value${quote} text, | ||||||
|   environment text, |   environment text, | ||||||
|   type text, |   type text, | ||||||
|   tag text |   tag text | ||||||
|  | |||||||
| @ -53,6 +53,9 @@ module.exports = strapi => { | |||||||
| 
 | 
 | ||||||
|           // Recursive to mask the private properties.
 |           // Recursive to mask the private properties.
 | ||||||
|           const mask = (payload) => { |           const mask = (payload) => { | ||||||
|  |             // Handle ORM toJSON() method to work on real JSON object.
 | ||||||
|  |             payload = payload.toJSON ? payload.toJSON() : payload; | ||||||
|  | 
 | ||||||
|             if (_.isArray(payload)) { |             if (_.isArray(payload)) { | ||||||
|               return payload.map(mask); |               return payload.map(mask); | ||||||
|             } else if (_.isPlainObject(payload)) { |             } else if (_.isPlainObject(payload)) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aurelsicoko
						Aurelsicoko