mirror of
https://github.com/strapi/strapi.git
synced 2025-09-01 12:53:03 +00:00
bookshelf clean populate
This commit is contained in:
parent
72c7203e18
commit
fd54e71baf
@ -7,11 +7,6 @@
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
find(params) {
|
find(params) {
|
||||||
// return strapi.query('article').find(params, {
|
return strapi.query('article').find(params);
|
||||||
// manyTags: () => {},
|
|
||||||
// ['linkedTags.linkedArticles.pic']: () => {},
|
|
||||||
// });
|
|
||||||
|
|
||||||
return strapi.query('article').find(params, ['ingredients']);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -461,8 +461,6 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
const groups = relations[key].toJSON().map(el => el.slice);
|
const groups = relations[key].toJSON().map(el => el.slice);
|
||||||
|
|
||||||
attrs[key] = repeatable === true ? groups : _.first(groups) || null;
|
attrs[key] = repeatable === true ? groups : _.first(groups) || null;
|
||||||
} else {
|
|
||||||
attrs[key] = repeatable === true ? [] : null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -523,6 +521,176 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
return attrs;
|
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 populateGroup = key => {
|
||||||
|
let paths = [];
|
||||||
|
const group = strapi.groups[definition.attributes[key].group];
|
||||||
|
const assocs = (group.associations || []).filter(
|
||||||
|
assoc => assoc.autoPopulate === true
|
||||||
|
);
|
||||||
|
|
||||||
|
// paths.push(`${key}.slice`);
|
||||||
|
assocs.forEach(assoc => {
|
||||||
|
if (isPolymorphic({ assoc })) {
|
||||||
|
const rel = formatPolymorphicPopulate({
|
||||||
|
assoc,
|
||||||
|
path: assoc.alias,
|
||||||
|
prefix: `${key}.slice.`,
|
||||||
|
});
|
||||||
|
|
||||||
|
paths.push(rel);
|
||||||
|
} else {
|
||||||
|
paths.push(`${key}.slice.${assoc.alias}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createGroupsPopulate = () => {
|
||||||
|
const groupsToPopulate = groupAttributes.reduce((acc, key) => {
|
||||||
|
const attribute = definition.attributes[key];
|
||||||
|
const autoPopulate = _.get(attribute, ['autoPopulate'], true);
|
||||||
|
|
||||||
|
if (autoPopulate === true) {
|
||||||
|
return acc.concat(populateGroup(key));
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return groupsToPopulate;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGroup = (def, key) =>
|
||||||
|
_.get(def, ['attributes', key, 'type']) === 'group';
|
||||||
|
|
||||||
|
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 groups 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 (isGroup(tmpModel, part)) {
|
||||||
|
tmpModel = strapi.groups[tmpModel.attributes[part].group];
|
||||||
|
// add group path and there relations / images
|
||||||
|
const path = `${prefix}${part}.slice`;
|
||||||
|
|
||||||
|
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.
|
// Initialize lifecycle callbacks.
|
||||||
loadedModel.initialize = function() {
|
loadedModel.initialize = function() {
|
||||||
const lifecycle = {
|
const lifecycle = {
|
||||||
@ -546,201 +714,21 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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 addPolymorphicRelated = path => {
|
|
||||||
// const assoc = definition.associations.find(
|
|
||||||
// assoc => assoc.alias === path || assoc.via === path
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (assoc && isPolymorphic({ assoc })) {
|
|
||||||
// return formatPolymorphicPopulate({
|
|
||||||
// assoc,
|
|
||||||
// path,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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 [path, ...extraAssocs];
|
|
||||||
// };
|
|
||||||
|
|
||||||
function 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), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateGroup(key) {
|
|
||||||
let paths = [];
|
|
||||||
const group = strapi.groups[definition.attributes[key].group];
|
|
||||||
const assocs = (group.associations || []).filter(
|
|
||||||
assoc => assoc.autoPopulate === true
|
|
||||||
);
|
|
||||||
|
|
||||||
assocs.forEach(assoc => {
|
|
||||||
if (isPolymorphic({ assoc })) {
|
|
||||||
const rel = formatPolymorphicPopulate({
|
|
||||||
assoc,
|
|
||||||
path: assoc.alias,
|
|
||||||
prefix: `${key}.slice.`,
|
|
||||||
});
|
|
||||||
|
|
||||||
paths.push(rel);
|
|
||||||
} else {
|
|
||||||
paths.push(`${key}.slice.${assoc.alias}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
paths.push(`${key}.slice`);
|
|
||||||
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createGroupsPopulate() {
|
|
||||||
const groupsToPopulate = groupAttributes.reduce((acc, key) => {
|
|
||||||
const attribute = definition.attributes[key];
|
|
||||||
const autoPopulate = _.get(attribute, ['autoPopulate'], true);
|
|
||||||
|
|
||||||
if (autoPopulate === true) {
|
|
||||||
return acc.concat(populateGroup(key));
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return groupsToPopulate;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isGroup = key => groupAttributes.includes(key);
|
|
||||||
|
|
||||||
function formatPopulateOptions(populate) {
|
|
||||||
// if groups are no
|
|
||||||
return populate
|
|
||||||
.reduce((acc, opt) => {
|
|
||||||
if (typeof opt === 'string') {
|
|
||||||
// split in parts and check if some parts of the path are morph or groups and update them
|
|
||||||
const parts = opt.split('.');
|
|
||||||
|
|
||||||
if (parts.length === 1) {
|
|
||||||
if (isGroup(opt)) {
|
|
||||||
// add group path and there relations / images
|
|
||||||
return acc.concat(populateGroup(opt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opt === 'object' && opt !== null) {
|
|
||||||
return acc.concat(opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [])
|
|
||||||
.reduce((acc, val) => acc.concat(val), []);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
|
// Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
|
||||||
// Apply only during fetching.
|
// Apply only during fetching.
|
||||||
this.on('fetching fetching:collection', (instance, attrs, options) => {
|
this.on('fetching fetching:collection', (instance, attrs, options) => {
|
||||||
// do not populate anything
|
// do not populate anything
|
||||||
if (options.withRelated === false) return;
|
if (options.withRelated === false) return;
|
||||||
|
if (options.isEager === true) return;
|
||||||
|
|
||||||
if (_.isNil(options.withRelated)) {
|
if (_.isNil(options.withRelated)) {
|
||||||
options.withRelated = []
|
options.withRelated = []
|
||||||
.concat(createGroupsPopulate())
|
.concat(createGroupsPopulate())
|
||||||
.concat(createAssociationPopulate());
|
.concat(createAssociationPopulate());
|
||||||
} else if (Array.isArray(options.withRelated)) {
|
} else {
|
||||||
options.withRelated = formatPopulateOptions(options.withRelated);
|
options.withRelated = formatPopulateOptions(options.withRelated);
|
||||||
} else if (_.isObject(options.withRelated)) {
|
|
||||||
options.withRelated = formatPopulateOptions([options.withRelated]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (_.isArray(options.withRelated)) {
|
|
||||||
// options.withRelated = options.withRelated
|
|
||||||
// .concat(groupAttributes.map(key => `${key}.slice`))
|
|
||||||
// .map(addPolymorphicRelated)
|
|
||||||
// .reduce((acc, paths) => acc.concat(paths), []);
|
|
||||||
// } else {
|
|
||||||
// options.withRelated = groupAttributes
|
|
||||||
// .map(key => `${key}.slice`)
|
|
||||||
// .map(addPolymorphicRelated)
|
|
||||||
// .reduce((acc, paths) => acc.concat(paths), []);
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
this.on('fetching fetching:collection', () => {
|
|
||||||
return _.isFunction(target[model.toLowerCase()]['beforeFetchAll'])
|
return _.isFunction(target[model.toLowerCase()]['beforeFetchAll'])
|
||||||
? target[model.toLowerCase()]['beforeFetchAll']
|
? target[model.toLowerCase()]['beforeFetchAll']
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
|
@ -19,11 +19,6 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
return model.attributes[key].type === 'group';
|
return model.attributes[key].type === 'group';
|
||||||
});
|
});
|
||||||
|
|
||||||
// default relations to populate
|
|
||||||
const defaultPopulate = model.associations
|
|
||||||
.filter(ast => ast.autoPopulate !== false)
|
|
||||||
.map(ast => ast.alias);
|
|
||||||
|
|
||||||
// Returns an object with relation keys only to create relations in DB
|
// Returns an object with relation keys only to create relations in DB
|
||||||
const pickRelations = values => {
|
const pickRelations = values => {
|
||||||
return _.pick(values, assocKeys);
|
return _.pick(values, assocKeys);
|
||||||
@ -58,7 +53,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entry = await model.forge(params).fetch({
|
const entry = await model.forge(params).fetch({
|
||||||
withRelated: _.isNil(populate) ? defaultPopulate : populate,
|
withRelated: populate,
|
||||||
});
|
});
|
||||||
|
|
||||||
return entry ? entry.toJSON() : null;
|
return entry ? entry.toJSON() : null;
|
||||||
@ -73,7 +68,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
return model
|
return model
|
||||||
.query(buildQuery({ model, filters }))
|
.query(buildQuery({ model, filters }))
|
||||||
.fetchAll({
|
.fetchAll({
|
||||||
withRelated: _.isNil(populate) ? defaultPopulate : populate,
|
withRelated: populate,
|
||||||
transacting,
|
transacting,
|
||||||
})
|
})
|
||||||
.then(results => results.toJSON());
|
.then(results => results.toJSON());
|
||||||
@ -199,9 +194,6 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
// Convert `params` object to filters compatible with Bookshelf.
|
// Convert `params` object to filters compatible with Bookshelf.
|
||||||
const filters = modelUtils.convertParams(modelKey, params);
|
const filters = modelUtils.convertParams(modelKey, params);
|
||||||
|
|
||||||
// Select field to populate.
|
|
||||||
const withRelated = populate || defaultPopulate;
|
|
||||||
|
|
||||||
return model
|
return model
|
||||||
.query(qb => {
|
.query(qb => {
|
||||||
buildSearchQuery(qb, model, params);
|
buildSearchQuery(qb, model, params);
|
||||||
@ -219,7 +211,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fetchAll({
|
.fetchAll({
|
||||||
withRelated,
|
withRelated: populate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user