mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 15:13:21 +00:00
Refactor populateFetch in its own module
This commit is contained in:
parent
2ee39bc9d8
commit
80fb99f38e
@ -7,7 +7,7 @@ const { getComponentAttributes } = require('./utils/attributes');
|
||||
const createComponentModels = async ({ model, definition, ORM, GLOBALS }) => {
|
||||
const { collectionName, primaryKey } = definition;
|
||||
|
||||
const componentAttributes = getComponentAttributes(definition.attributes);
|
||||
const componentAttributes = getComponentAttributes(definition);
|
||||
|
||||
if (componentAttributes.length > 0) {
|
||||
// create component model
|
||||
@ -59,7 +59,7 @@ const createComponentModels = async ({ model, definition, ORM, GLOBALS }) => {
|
||||
const createComponentJoinTables = async ({ definition, ORM }) => {
|
||||
const { collectionName, primaryKey } = definition;
|
||||
|
||||
const componentAttributes = getComponentAttributes(definition.attributes);
|
||||
const componentAttributes = getComponentAttributes(definition);
|
||||
|
||||
if (componentAttributes.length > 0) {
|
||||
const joinTable = `${collectionName}_components`;
|
||||
|
||||
@ -11,8 +11,25 @@ const {
|
||||
createComponentModels,
|
||||
} = require('./generate-component-relations');
|
||||
|
||||
const populateFetch = require('./populate');
|
||||
|
||||
const PIVOT_PREFIX = '_pivot_';
|
||||
|
||||
const LIFECYCLES = {
|
||||
creating: 'beforeCreate',
|
||||
created: 'afterCreate',
|
||||
destroying: 'beforeDestroy',
|
||||
destroyed: 'afterDestroy',
|
||||
updating: 'beforeUpdate',
|
||||
updated: 'afterUpdate',
|
||||
fetching: 'beforeFetch',
|
||||
'fetching:collection': 'beforeFetchAll',
|
||||
fetched: 'afterFetch',
|
||||
'fetched:collection': 'afterFetchAll',
|
||||
saving: 'beforeSave',
|
||||
saved: 'afterSave',
|
||||
};
|
||||
|
||||
const getDatabaseName = connection => {
|
||||
const dbName = _.get(connection.settings, 'database');
|
||||
const dbSchema = _.get(connection.settings, 'schema', 'public');
|
||||
@ -570,202 +587,12 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
return attrs;
|
||||
};
|
||||
|
||||
const findModelByAssoc = ({ assoc }) => {
|
||||
const target = assoc.collection || assoc.model;
|
||||
return assoc.plugin === 'admin'
|
||||
? strapi.admin.models[target]
|
||||
: assoc.plugin
|
||||
? strapi.plugins[assoc.plugin].models[target]
|
||||
: strapi.models[target];
|
||||
};
|
||||
|
||||
const isPolymorphic = ({ assoc }) => {
|
||||
return assoc.nature.toLowerCase().indexOf('morph') !== -1;
|
||||
};
|
||||
|
||||
const formatPolymorphicPopulate = ({ assoc, path, prefix = '' }) => {
|
||||
if (_.isString(path) && path === assoc.via) {
|
||||
return { [`related.${assoc.via}`]: () => {} };
|
||||
} else if (_.isString(path) && path === assoc.alias) {
|
||||
// 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');
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const createAssociationPopulate = () => {
|
||||
return definition.associations
|
||||
.filter(ast => ast.autoPopulate !== false)
|
||||
.map(assoc => {
|
||||
if (isPolymorphic({ assoc })) {
|
||||
return formatPolymorphicPopulate({
|
||||
assoc,
|
||||
path: assoc.alias,
|
||||
});
|
||||
}
|
||||
|
||||
let path = assoc.alias;
|
||||
let extraAssocs = [];
|
||||
if (assoc) {
|
||||
const assocModel = findModelByAssoc({ assoc });
|
||||
|
||||
extraAssocs = assocModel.associations
|
||||
.filter(assoc => isPolymorphic({ assoc }))
|
||||
.map(assoc =>
|
||||
formatPolymorphicPopulate({
|
||||
assoc,
|
||||
path: assoc.alias,
|
||||
prefix: `${path}.`,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return [assoc.alias, ...extraAssocs];
|
||||
})
|
||||
.reduce((acc, val) => acc.concat(val), []);
|
||||
};
|
||||
|
||||
const populateComponent = key => {
|
||||
const attr = definition.attributes[key];
|
||||
|
||||
if (attr.type === 'dynamiczone') return [`${key}.component`];
|
||||
|
||||
let paths = [];
|
||||
const component = strapi.components[attr.component];
|
||||
|
||||
const assocs = (component.associations || []).filter(
|
||||
assoc => assoc.autoPopulate === true
|
||||
);
|
||||
|
||||
// paths.push(`${key}.component`);
|
||||
assocs.forEach(assoc => {
|
||||
if (isPolymorphic({ assoc })) {
|
||||
const rel = formatPolymorphicPopulate({
|
||||
assoc,
|
||||
path: assoc.alias,
|
||||
prefix: `${key}.component.`,
|
||||
});
|
||||
|
||||
paths.push(rel);
|
||||
} else {
|
||||
paths.push(`${key}.component.${assoc.alias}`);
|
||||
}
|
||||
});
|
||||
|
||||
return [`${key}.component`, ...paths];
|
||||
};
|
||||
|
||||
const createComponentsPopulate = () => {
|
||||
const componentsToPopulate = componentAttributes.reduce((acc, key) => {
|
||||
const attribute = definition.attributes[key];
|
||||
const autoPopulate = _.get(attribute, ['autoPopulate'], true);
|
||||
|
||||
if (autoPopulate === true) {
|
||||
return acc.concat(populateComponent(key));
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return componentsToPopulate;
|
||||
};
|
||||
|
||||
const isComponent = (def, key) =>
|
||||
_.get(def, ['attributes', key, 'type']) === 'component';
|
||||
|
||||
const formatPopulateOptions = withRelated => {
|
||||
if (!Array.isArray(withRelated)) withRelated = [withRelated];
|
||||
|
||||
const obj = withRelated.reduce((acc, key) => {
|
||||
if (_.isString(key)) {
|
||||
acc[key] = () => {};
|
||||
return acc;
|
||||
}
|
||||
|
||||
return _.extend(acc, key);
|
||||
}, {});
|
||||
|
||||
// if components are no
|
||||
const finalObj = Object.keys(obj).reduce((acc, key) => {
|
||||
// check the key path and update it if necessary nothing more
|
||||
const parts = key.split('.');
|
||||
|
||||
let newKey;
|
||||
let prefix = '';
|
||||
let tmpModel = definition;
|
||||
for (let part of parts) {
|
||||
if (isComponent(tmpModel, part)) {
|
||||
tmpModel = strapi.components[tmpModel.attributes[part].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,
|
||||
path: assoc.alias,
|
||||
prefix,
|
||||
});
|
||||
|
||||
return _.extend(acc, path);
|
||||
}
|
||||
|
||||
newKey = `${prefix}${part}`;
|
||||
prefix = `${newKey}.`;
|
||||
}
|
||||
|
||||
acc[newKey] = obj[key];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return [finalObj];
|
||||
};
|
||||
|
||||
// Initialize lifecycle callbacks.
|
||||
loadedModel.initialize = function() {
|
||||
// Load bookshelf plugin arguments from model options
|
||||
this.constructor.__super__.initialize.apply(this, arguments);
|
||||
|
||||
const lifecycle = {
|
||||
creating: 'beforeCreate',
|
||||
created: 'afterCreate',
|
||||
destroying: 'beforeDestroy',
|
||||
destroyed: 'afterDestroy',
|
||||
updating: 'beforeUpdate',
|
||||
updated: 'afterUpdate',
|
||||
fetching: 'beforeFetch',
|
||||
'fetching:collection': 'beforeFetchAll',
|
||||
fetched: 'afterFetch',
|
||||
'fetched:collection': 'afterFetchAll',
|
||||
saving: 'beforeSave',
|
||||
saved: 'afterSave',
|
||||
};
|
||||
|
||||
_.forEach(lifecycle, (fn, key) => {
|
||||
_.forEach(LIFECYCLES, (fn, key) => {
|
||||
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
||||
this.on(key, target[model.toLowerCase()][fn]);
|
||||
}
|
||||
@ -774,19 +601,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
// Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
|
||||
// Apply only during fetching.
|
||||
this.on('fetching fetching:collection', (instance, attrs, options) => {
|
||||
// do not populate anything
|
||||
if (options.withRelated === false) return;
|
||||
if (options.isEager === true) return;
|
||||
|
||||
if (_.isNil(options.withRelated)) {
|
||||
options.withRelated = []
|
||||
.concat(createComponentsPopulate())
|
||||
.concat(createAssociationPopulate());
|
||||
} else if (_.isEmpty(options.withRelated)) {
|
||||
options.withRelated = createComponentsPopulate();
|
||||
} else {
|
||||
options.withRelated = formatPopulateOptions(options.withRelated);
|
||||
}
|
||||
populateFetch(definition, options);
|
||||
|
||||
return _.isFunction(target[model.toLowerCase()]['beforeFetchAll'])
|
||||
? target[model.toLowerCase()]['beforeFetchAll']
|
||||
|
||||
186
packages/strapi-connector-bookshelf/lib/populate.js
Normal file
186
packages/strapi-connector-bookshelf/lib/populate.js
Normal file
@ -0,0 +1,186 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const { getComponentAttributes } = 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(createComponentsPopulate(definition))
|
||||
.concat(createAssociationPopulate(definition));
|
||||
} else if (_.isEmpty(options.withRelated)) {
|
||||
options.withRelated = createComponentsPopulate(definition);
|
||||
} else {
|
||||
options.withRelated = formatPopulateOptions(
|
||||
definition,
|
||||
options.withRelated
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const isComponent = (def, key) =>
|
||||
_.get(def, ['attributes', key, 'type']) === 'component';
|
||||
|
||||
const createAssociationPopulate = definition => {
|
||||
return definition.associations
|
||||
.filter(ast => ast.autoPopulate !== false)
|
||||
.map(assoc => {
|
||||
if (isPolymorphic({ assoc })) {
|
||||
return formatPolymorphicPopulate({
|
||||
assoc,
|
||||
path: assoc.alias,
|
||||
});
|
||||
}
|
||||
|
||||
let path = assoc.alias;
|
||||
let extraAssocs = [];
|
||||
if (assoc) {
|
||||
const assocModel = findModelByAssoc({ assoc });
|
||||
|
||||
extraAssocs = assocModel.associations
|
||||
.filter(assoc => isPolymorphic({ assoc }))
|
||||
.map(assoc =>
|
||||
formatPolymorphicPopulate({
|
||||
assoc,
|
||||
path: assoc.alias,
|
||||
prefix: `${path}.`,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return [assoc.alias, ...extraAssocs];
|
||||
})
|
||||
.reduce((acc, val) => acc.concat(val), []);
|
||||
};
|
||||
|
||||
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);
|
||||
}, {});
|
||||
|
||||
// if components are no
|
||||
const finalObj = Object.keys(obj).reduce((acc, key) => {
|
||||
// check the key path and update it if necessary nothing more
|
||||
const parts = key.split('.');
|
||||
|
||||
let newKey;
|
||||
let prefix = '';
|
||||
let tmpModel = definition;
|
||||
for (let part of parts) {
|
||||
if (isComponent(tmpModel, part)) {
|
||||
tmpModel = strapi.components[tmpModel.attributes[part].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,
|
||||
path: assoc.alias,
|
||||
prefix,
|
||||
});
|
||||
|
||||
return _.extend(acc, path);
|
||||
}
|
||||
|
||||
newKey = `${prefix}${part}`;
|
||||
prefix = `${newKey}.`;
|
||||
}
|
||||
|
||||
acc[newKey] = obj[key];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return [finalObj];
|
||||
};
|
||||
|
||||
const populateComponent = (key, attr) => {
|
||||
if (attr.type === 'dynamiczone') return [`${key}.component`];
|
||||
|
||||
let paths = [];
|
||||
const component = strapi.components[attr.component];
|
||||
|
||||
const assocs = (component.associations || []).filter(
|
||||
assoc => assoc.autoPopulate === true
|
||||
);
|
||||
|
||||
// paths.push(`${key}.component`);
|
||||
assocs.forEach(assoc => {
|
||||
if (isPolymorphic({ assoc })) {
|
||||
const rel = formatPolymorphicPopulate({
|
||||
assoc,
|
||||
path: assoc.alias,
|
||||
prefix: `${key}.component.`,
|
||||
});
|
||||
|
||||
paths.push(rel);
|
||||
} else {
|
||||
paths.push(`${key}.component.${assoc.alias}`);
|
||||
}
|
||||
});
|
||||
|
||||
return [`${key}.component`, ...paths];
|
||||
};
|
||||
|
||||
const createComponentsPopulate = definition => {
|
||||
return getComponentAttributes(definition).reduce((acc, key) => {
|
||||
const attribute = definition.attributes[key];
|
||||
const autoPopulate = _.get(attribute, ['autoPopulate'], true);
|
||||
|
||||
if (autoPopulate === true) {
|
||||
return acc.concat(populateComponent(key, attribute));
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const formatPolymorphicPopulate = ({ assoc, path, prefix = '' }) => {
|
||||
if (_.isString(path) && path === assoc.via) {
|
||||
return { [`related.${assoc.via}`]: () => {} };
|
||||
} else if (_.isString(path) && path === assoc.alias) {
|
||||
// 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;
|
||||
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const findModelByAssoc = ({ assoc }) => {
|
||||
const target = assoc.collection || assoc.model;
|
||||
return assoc.plugin === 'admin'
|
||||
? strapi.admin.models[target]
|
||||
: assoc.plugin
|
||||
? strapi.plugins[assoc.plugin].models[target]
|
||||
: strapi.models[target];
|
||||
};
|
||||
|
||||
const isPolymorphic = ({ assoc }) => {
|
||||
return assoc.nature.toLowerCase().indexOf('morph') !== -1;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
findModelByAssoc,
|
||||
isPolymorphic,
|
||||
};
|
||||
@ -3,9 +3,9 @@
|
||||
/**
|
||||
* Returns the attribute keys of the component related attributes
|
||||
*/
|
||||
function getComponentAttributes(attributes) {
|
||||
return Object.keys(attributes).filter(key =>
|
||||
['component', 'dynamiczone'].includes(attributes[key].type)
|
||||
function getComponentAttributes(definition) {
|
||||
return Object.keys(definition.attributes).filter(key =>
|
||||
['component', 'dynamiczone'].includes(definition.attributes[key].type)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user