mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 01:47:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			209 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const _ = require('lodash');
 | |
| const { getComponentAttributes, isComponent } = require('./utils/attributes');
 | |
| const { findModelByAssoc, isPolymorphic } = require('./utils/associations');
 | |
| 
 | |
| /**
 | |
|  * Create utilities to populate a model on fetch
 | |
|  */
 | |
| 
 | |
| const populateFetch = (definition, options) => {
 | |
|   // do not populate anything
 | |
|   if (options.withRelated === false) return;
 | |
|   if (options.isEager === true) return;
 | |
| 
 | |
|   if (_.isNil(options.withRelated)) {
 | |
|     options.withRelated = []
 | |
|       .concat(populateComponents(definition))
 | |
|       .concat(populateAssociations(definition));
 | |
|   } else if (_.isEmpty(options.withRelated)) {
 | |
|     options.withRelated = populateComponents(definition);
 | |
|   } else {
 | |
|     options.withRelated = formatPopulateOptions(
 | |
|       definition,
 | |
|       options.withRelated
 | |
|     );
 | |
|   }
 | |
| };
 | |
| 
 | |
| const populateAssociations = (definition, { prefix = '' } = {}) => {
 | |
|   return definition.associations
 | |
|     .filter(ast => ast.autoPopulate !== false)
 | |
|     .map(assoc => {
 | |
|       if (isPolymorphic({ assoc })) {
 | |
|         return formatPolymorphicPopulate({
 | |
|           assoc,
 | |
|           prefix,
 | |
|         });
 | |
|       }
 | |
| 
 | |
|       return formatAssociationPopulate({ assoc, prefix });
 | |
|     })
 | |
|     .reduce((acc, val) => acc.concat(val), []);
 | |
| };
 | |
| 
 | |
| const populateBareAssociations = (definition, { prefix = '' } = {}) => {
 | |
|   return definition.associations
 | |
|     .filter(ast => ast.autoPopulate !== false)
 | |
|     .map(assoc => {
 | |
|       if (isPolymorphic({ assoc })) {
 | |
|         return formatPolymorphicPopulate({
 | |
|           assoc,
 | |
|           prefix,
 | |
|         });
 | |
|       }
 | |
| 
 | |
|       return `${prefix}${assoc.alias}`;
 | |
|     });
 | |
| };
 | |
| 
 | |
| const formatAssociationPopulate = ({ assoc, prefix = '' }) => {
 | |
|   const path = `${prefix}${assoc.alias}`;
 | |
|   const assocModel = findModelByAssoc({ assoc });
 | |
| 
 | |
|   const polyAssocs = assocModel.associations
 | |
|     .filter(assoc => isPolymorphic({ assoc }))
 | |
|     .map(assoc =>
 | |
|       formatPolymorphicPopulate({
 | |
|         assoc,
 | |
|         prefix: `${path}.`,
 | |
|       })
 | |
|     );
 | |
| 
 | |
|   const components = populateComponents(assocModel, { prefix: `${path}.` });
 | |
| 
 | |
|   return [path, ...polyAssocs, ...components];
 | |
| };
 | |
| 
 | |
| const populateComponents = (definition, { prefix = '' } = {}) => {
 | |
|   return getComponentAttributes(definition)
 | |
|     .map(key => {
 | |
|       const attribute = definition.attributes[key];
 | |
|       const autoPopulate = _.get(attribute, ['autoPopulate'], true);
 | |
| 
 | |
|       if (autoPopulate === true) {
 | |
|         return populateComponent(key, attribute, { prefix });
 | |
|       }
 | |
|     }, [])
 | |
|     .reduce((acc, val) => acc.concat(val), []);
 | |
| };
 | |
| 
 | |
| const populateComponent = (key, attr, { prefix = '' } = {}) => {
 | |
|   const path = `${prefix}${key}.component`;
 | |
|   const componentPrefix = `${path}.`;
 | |
| 
 | |
|   if (attr.type === 'dynamiczone') {
 | |
|     const componentKeys = attr.components;
 | |
| 
 | |
|     return componentKeys.reduce((acc, key) => {
 | |
|       const component = strapi.components[key];
 | |
|       const assocs = populateBareAssociations(component, {
 | |
|         prefix: componentPrefix,
 | |
|       });
 | |
| 
 | |
|       const components = populateComponents(component, {
 | |
|         prefix: componentPrefix,
 | |
|       });
 | |
| 
 | |
|       return acc.concat([path, ...assocs, ...components]);
 | |
|     }, []);
 | |
|   }
 | |
| 
 | |
|   const component = strapi.components[attr.component];
 | |
|   const assocs = populateBareAssociations(component, {
 | |
|     prefix: componentPrefix,
 | |
|   });
 | |
| 
 | |
|   const components = populateComponents(component, {
 | |
|     prefix: componentPrefix,
 | |
|   });
 | |
| 
 | |
|   return [path, ...assocs, ...components];
 | |
| };
 | |
| 
 | |
| const formatPopulateOptions = (definition, withRelated) => {
 | |
|   if (!Array.isArray(withRelated)) withRelated = [withRelated];
 | |
| 
 | |
|   const obj = withRelated.reduce((acc, key) => {
 | |
|     if (_.isString(key)) {
 | |
|       acc[key] = () => {};
 | |
|       return acc;
 | |
|     }
 | |
| 
 | |
|     return _.extend(acc, key);
 | |
|   }, {});
 | |
| 
 | |
|   const finalObj = Object.keys(obj).reduce((acc, key) => {
 | |
|     // check the key path and update it if necessary
 | |
|     const parts = key.split('.');
 | |
| 
 | |
|     let newKey;
 | |
|     let prefix = '';
 | |
|     let tmpModel = definition;
 | |
|     for (let part of parts) {
 | |
|       const attr = tmpModel.attributes[part];
 | |
| 
 | |
|       if (isComponent(tmpModel, part)) {
 | |
|         if (attr.type === 'dynamiczone') {
 | |
|           const path = `${prefix}${part}.component`;
 | |
|           newKey = path;
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         tmpModel = strapi.components[attr.component];
 | |
|         // add component path and there relations / images
 | |
|         const path = `${prefix}${part}.component`;
 | |
| 
 | |
|         newKey = path;
 | |
|         prefix = `${path}.`;
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       const assoc = tmpModel.associations.find(
 | |
|         association => association.alias === part
 | |
|       );
 | |
| 
 | |
|       if (!assoc) return acc;
 | |
| 
 | |
|       tmpModel = findModelByAssoc({ assoc });
 | |
| 
 | |
|       if (isPolymorphic({ assoc })) {
 | |
|         const path = formatPolymorphicPopulate({
 | |
|           assoc,
 | |
|           prefix,
 | |
|         });
 | |
| 
 | |
|         return _.extend(acc, path);
 | |
|       }
 | |
| 
 | |
|       newKey = `${prefix}${part}`;
 | |
|       prefix = `${newKey}.`;
 | |
|     }
 | |
| 
 | |
|     acc[newKey] = obj[key];
 | |
|     return acc;
 | |
|   }, {});
 | |
| 
 | |
|   return [finalObj];
 | |
| };
 | |
| 
 | |
| const formatPolymorphicPopulate = ({ assoc, prefix = '' }) => {
 | |
|   // MorphTo side.
 | |
|   if (assoc.related) {
 | |
|     return { [`${prefix}${assoc.alias}.related`]: () => {} };
 | |
|   }
 | |
| 
 | |
|   // oneToMorph or manyToMorph side.
 | |
|   // Retrieve collection name because we are using it to build our hidden model.
 | |
|   const model = findModelByAssoc({ assoc });
 | |
| 
 | |
|   return {
 | |
|     [`${prefix}${assoc.alias}.${model.collectionName}`]: function(query) {
 | |
|       query.orderBy('created_at', 'desc');
 | |
|     },
 | |
|   };
 | |
| };
 | |
| 
 | |
| module.exports = populateFetch;
 | 
