mirror of
https://github.com/strapi/strapi.git
synced 2025-08-31 04:03:50 +00:00
bookshelf clean populate
This commit is contained in:
parent
72c7203e18
commit
fd54e71baf
@ -7,11 +7,6 @@
|
||||
|
||||
module.exports = {
|
||||
find(params) {
|
||||
// return strapi.query('article').find(params, {
|
||||
// manyTags: () => {},
|
||||
// ['linkedTags.linkedArticles.pic']: () => {},
|
||||
// });
|
||||
|
||||
return strapi.query('article').find(params, ['ingredients']);
|
||||
return strapi.query('article').find(params);
|
||||
},
|
||||
};
|
||||
|
@ -461,8 +461,6 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
const groups = relations[key].toJSON().map(el => el.slice);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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.
|
||||
loadedModel.initialize = function() {
|
||||
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.
|
||||
// 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(createGroupsPopulate())
|
||||
.concat(createAssociationPopulate());
|
||||
} else if (Array.isArray(options.withRelated)) {
|
||||
} else {
|
||||
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'])
|
||||
? target[model.toLowerCase()]['beforeFetchAll']
|
||||
: Promise.resolve();
|
||||
|
@ -19,11 +19,6 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
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
|
||||
const pickRelations = values => {
|
||||
return _.pick(values, assocKeys);
|
||||
@ -58,7 +53,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
}
|
||||
|
||||
const entry = await model.forge(params).fetch({
|
||||
withRelated: _.isNil(populate) ? defaultPopulate : populate,
|
||||
withRelated: populate,
|
||||
});
|
||||
|
||||
return entry ? entry.toJSON() : null;
|
||||
@ -73,7 +68,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
return model
|
||||
.query(buildQuery({ model, filters }))
|
||||
.fetchAll({
|
||||
withRelated: _.isNil(populate) ? defaultPopulate : populate,
|
||||
withRelated: populate,
|
||||
transacting,
|
||||
})
|
||||
.then(results => results.toJSON());
|
||||
@ -199,9 +194,6 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
// Convert `params` object to filters compatible with Bookshelf.
|
||||
const filters = modelUtils.convertParams(modelKey, params);
|
||||
|
||||
// Select field to populate.
|
||||
const withRelated = populate || defaultPopulate;
|
||||
|
||||
return model
|
||||
.query(qb => {
|
||||
buildSearchQuery(qb, model, params);
|
||||
@ -219,7 +211,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
}
|
||||
})
|
||||
.fetchAll({
|
||||
withRelated,
|
||||
withRelated: populate,
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user