mirror of
https://github.com/strapi/strapi.git
synced 2025-12-16 09:45:08 +00:00
Merge pull request #5553 from strapi/media-lib/ordering
Keep media order set on creation
This commit is contained in:
commit
dbaa1848dd
@ -44,7 +44,6 @@
|
||||
"max": 35.12
|
||||
},
|
||||
"address": {
|
||||
"required": true,
|
||||
"model": "address"
|
||||
},
|
||||
"cover": {
|
||||
|
||||
@ -416,6 +416,9 @@ module.exports = async ({ ORM, loadedModel, definition, connection, model }) =>
|
||||
[definition.attributes[morphRelation.alias].filter]: {
|
||||
type: 'text',
|
||||
},
|
||||
order: {
|
||||
type: 'integer',
|
||||
},
|
||||
};
|
||||
|
||||
if (connection.options && connection.options.autoMigration !== false) {
|
||||
@ -423,7 +426,7 @@ module.exports = async ({ ORM, loadedModel, definition, connection, model }) =>
|
||||
}
|
||||
}
|
||||
|
||||
// Equilize many to many releations
|
||||
// Equilize many to many relations
|
||||
const manyRelations = definition.associations.filter(({ nature }) =>
|
||||
['manyToMany', 'manyWay'].includes(nature)
|
||||
);
|
||||
|
||||
@ -142,7 +142,11 @@ module.exports = ({ models, target }, ctx) => {
|
||||
}
|
||||
|
||||
const { nature, verbose } =
|
||||
utilsModels.getNature(details, name, undefined, model.toLowerCase()) || {};
|
||||
utilsModels.getNature({
|
||||
attribute: details,
|
||||
attributeName: name,
|
||||
modelName: model.toLowerCase(),
|
||||
}) || {};
|
||||
|
||||
// Build associations key
|
||||
utilsModels.defineAssociations(model.toLowerCase(), definition, details, name);
|
||||
@ -302,6 +306,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
: strapi.models[details.model];
|
||||
|
||||
const globalId = `${model.collectionName}_morph`;
|
||||
const filter = _.get(model, ['attributes', details.via, 'filter'], 'field');
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.morphOne(
|
||||
@ -309,7 +314,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
details.via,
|
||||
`${definition.collectionName}`
|
||||
).query(qb => {
|
||||
qb.where(_.get(model, ['attributes', details.via, 'filter'], 'field'), name);
|
||||
qb.where(filter, name);
|
||||
});
|
||||
};
|
||||
break;
|
||||
@ -320,6 +325,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
: strapi.models[details.collection];
|
||||
|
||||
const globalId = `${collection.collectionName}_morph`;
|
||||
const filter = _.get(model, ['attributes', details.via, 'filter'], 'field');
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.morphMany(
|
||||
@ -327,7 +333,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
details.via,
|
||||
`${definition.collectionName}`
|
||||
).query(qb => {
|
||||
qb.where(_.get(model, ['attributes', details.via, 'filter'], 'field'), name);
|
||||
qb.where(filter, name).orderBy('order');
|
||||
});
|
||||
};
|
||||
break;
|
||||
@ -650,6 +656,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
// Push attributes to be aware of model schema.
|
||||
target[model]._attributes = definition.attributes;
|
||||
target[model].updateRelations = relations.update;
|
||||
target[model].deleteRelations = relations.deleteRelations;
|
||||
|
||||
await buildDatabaseSchema({
|
||||
ORM,
|
||||
|
||||
@ -132,26 +132,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const values = {};
|
||||
model.associations.map(association => {
|
||||
switch (association.nature) {
|
||||
case 'oneWay':
|
||||
case 'oneToOne':
|
||||
case 'manyToOne':
|
||||
case 'oneToManyMorph':
|
||||
values[association.alias] = null;
|
||||
break;
|
||||
case 'manyWay':
|
||||
case 'oneToMany':
|
||||
case 'manyToMany':
|
||||
case 'manyToManyMorph':
|
||||
values[association.alias] = [];
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
await model.updateRelations({ [model.primaryKey]: id, values }, { transacting });
|
||||
await model.deleteRelations(id, { transacting });
|
||||
|
||||
const runDelete = async trx => {
|
||||
await deleteComponents(entry, { transacting: trx });
|
||||
|
||||
@ -12,27 +12,15 @@ const {
|
||||
models: { getValuePrimaryKey },
|
||||
} = require('strapi-utils');
|
||||
|
||||
const transformToArrayID = (array, association) => {
|
||||
const transformToArrayID = array => {
|
||||
if (_.isArray(array)) {
|
||||
array = array.map(value => {
|
||||
if (_.isPlainObject(value)) {
|
||||
return value._id || value.id || false;
|
||||
return array
|
||||
.map(value => _.get(value, 'id') || value)
|
||||
.filter(n => n)
|
||||
.map(val => _.toString(val));
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
return array.filter(n => n);
|
||||
}
|
||||
|
||||
if (
|
||||
association.type === 'model' ||
|
||||
(association.type === 'collection' && _.isObject(array))
|
||||
) {
|
||||
return _.isEmpty(_.toString(array)) ? [] : transformToArrayID([array]);
|
||||
}
|
||||
|
||||
return [];
|
||||
return transformToArrayID([array]);
|
||||
};
|
||||
|
||||
const getModel = (model, plugin) => {
|
||||
@ -45,6 +33,39 @@ const getModel = (model, plugin) => {
|
||||
|
||||
const removeUndefinedKeys = obj => _.pickBy(obj, _.negate(_.isUndefined));
|
||||
|
||||
const addRelationMorph = async (model, { params, transacting } = {}) => {
|
||||
return await model.morph.forge().save(
|
||||
{
|
||||
[`${model.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field,
|
||||
order: params.order,
|
||||
},
|
||||
{ transacting }
|
||||
);
|
||||
};
|
||||
|
||||
const removeRelationMorph = async (model, { params, transacting } = {}) => {
|
||||
return await model.morph
|
||||
.forge()
|
||||
.where(
|
||||
_.omitBy(
|
||||
{
|
||||
[`${model.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field,
|
||||
},
|
||||
_.isUndefined
|
||||
)
|
||||
)
|
||||
.destroy({
|
||||
require: false,
|
||||
transacting,
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
async findOne(params, populate, { transacting } = {}) {
|
||||
const record = await this.forge({
|
||||
@ -59,17 +80,12 @@ module.exports = {
|
||||
// Retrieve data manually.
|
||||
if (_.isEmpty(populate)) {
|
||||
const arrayOfPromises = this.associations
|
||||
.filter(association =>
|
||||
['manyMorphToOne', 'manyMorphToMany'].includes(association.nature)
|
||||
)
|
||||
.filter(association => ['manyMorphToOne', 'manyMorphToMany'].includes(association.nature))
|
||||
.map(() => {
|
||||
return this.morph
|
||||
.forge()
|
||||
.where({
|
||||
[`${this.collectionName}_id`]: getValuePrimaryKey(
|
||||
params,
|
||||
this.primaryKey
|
||||
),
|
||||
[`${this.collectionName}_id`]: getValuePrimaryKey(params, this.primaryKey),
|
||||
})
|
||||
.fetchAll({
|
||||
transacting,
|
||||
@ -94,32 +110,20 @@ module.exports = {
|
||||
});
|
||||
|
||||
// Only update fields which are on this document.
|
||||
const values =
|
||||
params.parseRelationships === false
|
||||
? params.values
|
||||
: Object.keys(removeUndefinedKeys(params.values)).reduce(
|
||||
(acc, current) => {
|
||||
const values = Object.keys(removeUndefinedKeys(params.values)).reduce((acc, current) => {
|
||||
const property = params.values[current];
|
||||
const association = this.associations.filter(
|
||||
x => x.alias === current
|
||||
)[0];
|
||||
const association = this.associations.filter(x => x.alias === current)[0];
|
||||
const details = this._attributes[current];
|
||||
|
||||
if (!association && _.get(details, 'isVirtual') !== true) {
|
||||
return _.set(acc, current, property);
|
||||
}
|
||||
|
||||
const assocModel = getModel(
|
||||
details.model || details.collection,
|
||||
details.plugin
|
||||
);
|
||||
const assocModel = getModel(details.model || details.collection, details.plugin);
|
||||
|
||||
switch (association.nature) {
|
||||
case 'oneWay': {
|
||||
return _.set(
|
||||
acc,
|
||||
current,
|
||||
_.get(property, assocModel.primaryKey, property)
|
||||
);
|
||||
return _.set(acc, current, _.get(property, assocModel.primaryKey, property));
|
||||
}
|
||||
case 'oneToOne': {
|
||||
if (response[current] === property) return acc;
|
||||
@ -158,9 +162,7 @@ module.exports = {
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel
|
||||
.where({ [this.primaryKey]: property })
|
||||
.save(
|
||||
return assocModel.where({ [this.primaryKey]: property }).save(
|
||||
{ [details.via]: primaryKeyValue },
|
||||
{
|
||||
method: 'update',
|
||||
@ -180,16 +182,9 @@ module.exports = {
|
||||
|
||||
// set relation to null for all the ids not in the list
|
||||
const currentIds = response[current];
|
||||
const toRemove = _.differenceWith(
|
||||
currentIds,
|
||||
property,
|
||||
(a, b) => {
|
||||
return (
|
||||
`${a[assocModel.primaryKey] || a}` ===
|
||||
`${b[assocModel.primaryKey] || b}`
|
||||
);
|
||||
}
|
||||
);
|
||||
const toRemove = _.differenceWith(currentIds, property, (a, b) => {
|
||||
return `${a[assocModel.primaryKey] || a}` === `${b[assocModel.primaryKey] || b}`;
|
||||
});
|
||||
|
||||
const updatePromise = assocModel
|
||||
.where(
|
||||
@ -228,25 +223,15 @@ module.exports = {
|
||||
return acc;
|
||||
}
|
||||
case 'manyToOne': {
|
||||
return _.set(
|
||||
acc,
|
||||
current,
|
||||
_.get(property, assocModel.primaryKey, property)
|
||||
);
|
||||
return _.set(acc, current, _.get(property, assocModel.primaryKey, property));
|
||||
}
|
||||
case 'manyWay':
|
||||
case 'manyToMany': {
|
||||
const currentValue = transformToArrayID(
|
||||
response[current],
|
||||
association
|
||||
).map(id => id.toString());
|
||||
const storedValue = transformToArrayID(
|
||||
params.values[current],
|
||||
association
|
||||
).map(id => id.toString());
|
||||
const storedValue = transformToArrayID(response[current]);
|
||||
const currentValue = transformToArrayID(params.values[current]);
|
||||
|
||||
const toAdd = _.difference(storedValue, currentValue);
|
||||
const toRemove = _.difference(currentValue, storedValue);
|
||||
const toAdd = _.difference(currentValue, storedValue);
|
||||
const toRemove = _.difference(storedValue, currentValue);
|
||||
|
||||
const collection = this.forge({
|
||||
[this.primaryKey]: primaryKeyValue,
|
||||
@ -259,122 +244,123 @@ module.exports = {
|
||||
relationUpdates.push(updatePromise);
|
||||
return acc;
|
||||
}
|
||||
// media -> model
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne':
|
||||
case 'manyMorphToOne': {
|
||||
// Update the relational array.
|
||||
params.values[current].forEach(obj => {
|
||||
const model = strapi.getModel(
|
||||
const refs = params.values[current];
|
||||
|
||||
if (Array.isArray(refs) && refs.length === 0) {
|
||||
// clear related
|
||||
relationUpdates.push(
|
||||
removeRelationMorph(this, { params: { id: primaryKeyValue }, transacting })
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
refs.forEach(obj => {
|
||||
const targetModel = strapi.getModel(
|
||||
obj.ref,
|
||||
obj.source && obj.source !== 'content-manager'
|
||||
? obj.source
|
||||
: null
|
||||
obj.source !== 'content-manager' ? obj.source : null
|
||||
);
|
||||
|
||||
const reverseAssoc = model.associations.find(
|
||||
assoc => assoc.alias === obj.field
|
||||
);
|
||||
const reverseAssoc = targetModel.associations.find(assoc => assoc.alias === obj.field);
|
||||
|
||||
// Remove existing relationship because only one file
|
||||
// can be related to this field.
|
||||
if (
|
||||
reverseAssoc &&
|
||||
reverseAssoc.nature === 'oneToManyMorph'
|
||||
) {
|
||||
if (reverseAssoc && reverseAssoc.nature === 'oneToManyMorph') {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph
|
||||
.call(
|
||||
this,
|
||||
{
|
||||
removeRelationMorph(this, {
|
||||
params: {
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
ref: targetModel.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
},
|
||||
{ transacting }
|
||||
)
|
||||
.then(() =>
|
||||
module.exports.addRelationMorph.call(
|
||||
this,
|
||||
{
|
||||
transacting,
|
||||
}).then(() =>
|
||||
addRelationMorph(this, {
|
||||
params: {
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
ref: targetModel.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
order: 1,
|
||||
},
|
||||
{ transacting }
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
relationUpdates.push(
|
||||
module.exports.addRelationMorph.call(
|
||||
this,
|
||||
{
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
},
|
||||
{ transacting }
|
||||
transacting,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const addRelation = async () => {
|
||||
const maxOrder = await this.morph
|
||||
.query(qb => {
|
||||
qb.max('order as order').where({
|
||||
[`${association.alias}_id`]: obj.refId,
|
||||
[`${association.alias}_type`]: targetModel.collectionName,
|
||||
field: obj.field,
|
||||
});
|
||||
})
|
||||
.fetch({ transacting });
|
||||
|
||||
const { order = 0 } = maxOrder.toJSON();
|
||||
|
||||
await addRelationMorph(this, {
|
||||
params: {
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: targetModel.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
order: order + 1,
|
||||
},
|
||||
transacting,
|
||||
});
|
||||
};
|
||||
|
||||
relationUpdates.push(addRelation());
|
||||
});
|
||||
break;
|
||||
}
|
||||
// model -> media
|
||||
case 'oneToManyMorph':
|
||||
case 'manyToManyMorph': {
|
||||
// Compare array of ID to find deleted files.
|
||||
const currentValue = transformToArrayID(
|
||||
response[current],
|
||||
association
|
||||
).map(id => id.toString());
|
||||
const storedValue = transformToArrayID(
|
||||
params.values[current],
|
||||
association
|
||||
).map(id => id.toString());
|
||||
const currentValue = transformToArrayID(params.values[current]);
|
||||
|
||||
const toAdd = _.difference(storedValue, currentValue);
|
||||
const toRemove = _.difference(currentValue, storedValue);
|
||||
const model = getModel(details.collection || details.model, details.plugin);
|
||||
|
||||
const model = getModel(
|
||||
details.collection || details.model,
|
||||
details.plugin
|
||||
);
|
||||
|
||||
toAdd.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.addRelationMorph.call(
|
||||
model,
|
||||
{
|
||||
id,
|
||||
const promise = removeRelationMorph(model, {
|
||||
params: {
|
||||
alias: association.via,
|
||||
ref: this.collectionName,
|
||||
refId: response.id,
|
||||
field: association.alias,
|
||||
},
|
||||
{ transacting }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Update the relational array.
|
||||
toRemove.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph.call(
|
||||
model,
|
||||
{
|
||||
transacting,
|
||||
}).then(() => {
|
||||
return Promise.all(
|
||||
currentValue.map((id, idx) => {
|
||||
return addRelationMorph(model, {
|
||||
params: {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.collectionName,
|
||||
refId: response.id,
|
||||
field: association.alias,
|
||||
order: idx + 1,
|
||||
},
|
||||
{ transacting }
|
||||
)
|
||||
transacting,
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
relationUpdates.push(promise);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'oneMorphToOne':
|
||||
@ -385,9 +371,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}, {});
|
||||
|
||||
await Promise.all(relationUpdates);
|
||||
|
||||
@ -410,52 +394,29 @@ module.exports = {
|
||||
return result && result.toJSON ? result.toJSON() : result;
|
||||
},
|
||||
|
||||
async addRelationMorph(params, { transacting } = {}) {
|
||||
const record = await this.morph
|
||||
.forge()
|
||||
.where({
|
||||
[`${this.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field,
|
||||
})
|
||||
.fetch({
|
||||
transacting,
|
||||
});
|
||||
deleteRelations(id, { transacting }) {
|
||||
const values = {};
|
||||
|
||||
const entry = record ? record.toJSON() : record;
|
||||
|
||||
if (entry) {
|
||||
return Promise.resolve();
|
||||
this.associations.map(association => {
|
||||
switch (association.nature) {
|
||||
case 'oneWay':
|
||||
case 'oneToOne':
|
||||
case 'manyToOne':
|
||||
case 'oneToManyMorph':
|
||||
values[association.alias] = null;
|
||||
break;
|
||||
case 'manyWay':
|
||||
case 'oneToMany':
|
||||
case 'manyToMany':
|
||||
case 'manyToManyMorph':
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne':
|
||||
values[association.alias] = [];
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return await this.morph
|
||||
.forge({
|
||||
[`${this.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field,
|
||||
})
|
||||
.save(null, { transacting });
|
||||
},
|
||||
|
||||
async removeRelationMorph(params, { transacting } = {}) {
|
||||
return await this.morph
|
||||
.forge()
|
||||
.where(
|
||||
_.omitBy(
|
||||
{
|
||||
[`${this.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field,
|
||||
},
|
||||
_.isUndefined
|
||||
)
|
||||
)
|
||||
.destroy({
|
||||
require: false,
|
||||
transacting,
|
||||
});
|
||||
|
||||
return this.updateRelations({ [this.primaryKey]: id, values }, { transacting });
|
||||
},
|
||||
};
|
||||
|
||||
@ -219,6 +219,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
virtuals: true,
|
||||
transform: function(doc, returned) {
|
||||
// Remover $numberDecimal nested property.
|
||||
|
||||
Object.keys(returned)
|
||||
.filter(key => returned[key] instanceof mongoose.Types.Decimal128)
|
||||
.forEach(key => {
|
||||
@ -239,11 +240,13 @@ module.exports = ({ models, target }, ctx) => {
|
||||
break;
|
||||
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne':
|
||||
case 'manyMorphToOne': {
|
||||
returned[association.alias] = returned[association.alias].map(obj =>
|
||||
refToStrapiRef(obj)
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
@ -297,6 +300,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
// Push attributes to be aware of model schema.
|
||||
target[model]._attributes = definition.attributes;
|
||||
target[model].updateRelations = relations.update;
|
||||
target[model].deleteRelations = relations.deleteRelations;
|
||||
});
|
||||
};
|
||||
|
||||
@ -308,20 +312,8 @@ const createOnFetchPopulateFn = ({ morphAssociations, componentAttributes, defin
|
||||
const { alias, nature } = association;
|
||||
|
||||
if (['oneToManyMorph', 'manyToManyMorph'].includes(nature)) {
|
||||
this.populate({
|
||||
path: alias,
|
||||
match: {
|
||||
[`${association.via}.${association.filter}`]: association.alias,
|
||||
[`${association.via}.kind`]: definition.globalId,
|
||||
},
|
||||
options: {
|
||||
sort: '-createdAt',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (populatedPaths.includes(alias)) {
|
||||
this.populate(alias);
|
||||
} else if (populatedPaths.includes(alias)) {
|
||||
_.set(this._mongooseOptions.populate, [alias, 'path'], `${alias}.ref`);
|
||||
}
|
||||
});
|
||||
@ -343,48 +335,52 @@ const createOnFetchPopulateFn = ({ morphAssociations, componentAttributes, defin
|
||||
|
||||
const buildRelation = ({ definition, model, instance, attribute, name }) => {
|
||||
const { nature, verbose } =
|
||||
utilsModels.getNature(attribute, name, undefined, model.toLowerCase()) || {};
|
||||
utilsModels.getNature({
|
||||
attribute,
|
||||
attributeName: name,
|
||||
modelName: model.toLowerCase(),
|
||||
}) || {};
|
||||
|
||||
// Build associations key
|
||||
utilsModels.defineAssociations(model.toLowerCase(), definition, attribute, name);
|
||||
|
||||
const getRef = (name, plugin) => {
|
||||
return plugin ? strapi.plugins[plugin].models[name].globalId : strapi.models[name].globalId;
|
||||
};
|
||||
|
||||
const setField = (name, val) => {
|
||||
definition.loadedModel[name] = val;
|
||||
};
|
||||
|
||||
const { ObjectId } = instance.Schema.Types;
|
||||
|
||||
switch (verbose) {
|
||||
case 'hasOne': {
|
||||
const ref = attribute.plugin
|
||||
? strapi.plugins[attribute.plugin].models[attribute.model].globalId
|
||||
: strapi.models[attribute.model].globalId;
|
||||
const ref = getRef(attribute.model, attribute.plugin);
|
||||
|
||||
setField(name, { type: ObjectId, ref });
|
||||
|
||||
definition.loadedModel[name] = {
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'hasMany': {
|
||||
const FK = _.find(definition.associations, {
|
||||
alias: name,
|
||||
});
|
||||
const ref = attribute.plugin
|
||||
? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
|
||||
: strapi.models[attribute.collection].globalId;
|
||||
|
||||
const ref = getRef(attribute.collection, attribute.plugin);
|
||||
|
||||
if (FK) {
|
||||
definition.loadedModel[name] = {
|
||||
setField(name, {
|
||||
type: 'virtual',
|
||||
ref,
|
||||
via: FK.via,
|
||||
justOne: false,
|
||||
};
|
||||
});
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
attribute.isVirtual = true;
|
||||
} else {
|
||||
definition.loadedModel[name] = [
|
||||
{
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref,
|
||||
},
|
||||
];
|
||||
setField(name, [{ type: ObjectId, ref }]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -392,9 +388,8 @@ const buildRelation = ({ definition, model, instance, attribute, name }) => {
|
||||
const FK = _.find(definition.associations, {
|
||||
alias: name,
|
||||
});
|
||||
const ref = attribute.plugin
|
||||
? strapi.plugins[attribute.plugin].models[attribute.model].globalId
|
||||
: strapi.models[attribute.model].globalId;
|
||||
|
||||
const ref = getRef(attribute.model, attribute.plugin);
|
||||
|
||||
if (
|
||||
FK &&
|
||||
@ -403,38 +398,26 @@ const buildRelation = ({ definition, model, instance, attribute, name }) => {
|
||||
FK.nature !== 'oneWay' &&
|
||||
FK.nature !== 'oneToMorph'
|
||||
) {
|
||||
definition.loadedModel[name] = {
|
||||
setField(name, {
|
||||
type: 'virtual',
|
||||
ref,
|
||||
via: FK.via,
|
||||
justOne: true,
|
||||
};
|
||||
});
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
attribute.isVirtual = true;
|
||||
} else {
|
||||
definition.loadedModel[name] = {
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref,
|
||||
};
|
||||
setField(name, { type: ObjectId, ref });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'belongsToMany': {
|
||||
const targetModel = attribute.plugin
|
||||
? strapi.plugins[attribute.plugin].models[attribute.collection]
|
||||
: strapi.models[attribute.collection];
|
||||
|
||||
const ref = targetModel.globalId;
|
||||
const ref = getRef(attribute.collection, attribute.plugin);
|
||||
|
||||
if (nature === 'manyWay') {
|
||||
definition.loadedModel[name] = [
|
||||
{
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref,
|
||||
},
|
||||
];
|
||||
setField(name, [{ type: ObjectId, ref }]);
|
||||
} else {
|
||||
const FK = _.find(definition.associations, {
|
||||
alias: name,
|
||||
@ -442,84 +425,47 @@ const buildRelation = ({ definition, model, instance, attribute, name }) => {
|
||||
|
||||
// One-side of the relationship has to be a virtual field to be bidirectional.
|
||||
if ((FK && _.isUndefined(FK.via)) || attribute.dominant !== true) {
|
||||
definition.loadedModel[name] = {
|
||||
setField(name, {
|
||||
type: 'virtual',
|
||||
ref,
|
||||
via: FK.via,
|
||||
};
|
||||
});
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
attribute.isVirtual = true;
|
||||
} else {
|
||||
definition.loadedModel[name] = [
|
||||
{
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
ref,
|
||||
},
|
||||
];
|
||||
setField(name, [{ type: ObjectId, ref }]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'morphOne': {
|
||||
const FK = _.find(definition.associations, {
|
||||
alias: name,
|
||||
});
|
||||
const ref = attribute.plugin
|
||||
? strapi.plugins[attribute.plugin].models[attribute.model].globalId
|
||||
: strapi.models[attribute.model].globalId;
|
||||
|
||||
definition.loadedModel[name] = {
|
||||
type: 'virtual',
|
||||
ref,
|
||||
via: `${FK.via}.ref`,
|
||||
justOne: true,
|
||||
};
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
attribute.isVirtual = true;
|
||||
const ref = getRef(attribute.model, attribute.plugin);
|
||||
setField(name, { type: ObjectId, ref });
|
||||
break;
|
||||
}
|
||||
case 'morphMany': {
|
||||
const FK = _.find(definition.associations, {
|
||||
alias: name,
|
||||
});
|
||||
const ref = attribute.plugin
|
||||
? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
|
||||
: strapi.models[attribute.collection].globalId;
|
||||
|
||||
definition.loadedModel[name] = {
|
||||
type: 'virtual',
|
||||
ref,
|
||||
via: `${FK.via}.ref`,
|
||||
};
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
attribute.isVirtual = true;
|
||||
const ref = getRef(attribute.collection, attribute.plugin);
|
||||
setField(name, [{ type: ObjectId, ref }]);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'belongsToMorph': {
|
||||
definition.loadedModel[name] = {
|
||||
setField(name, {
|
||||
kind: String,
|
||||
[attribute.filter]: String,
|
||||
ref: {
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
refPath: `${name}.kind`,
|
||||
},
|
||||
};
|
||||
ref: { type: ObjectId, refPath: `${name}.kind` },
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'belongsToManyMorph': {
|
||||
definition.loadedModel[name] = [
|
||||
setField(name, [
|
||||
{
|
||||
kind: String,
|
||||
[attribute.filter]: String,
|
||||
ref: {
|
||||
type: instance.Schema.Types.ObjectId,
|
||||
refPath: `${name}.kind`,
|
||||
ref: { type: ObjectId, refPath: `${name}.kind` },
|
||||
},
|
||||
},
|
||||
];
|
||||
]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@ -474,29 +474,7 @@ module.exports = ({ model, modelKey, strapi }) => {
|
||||
|
||||
await deleteComponents(entry);
|
||||
|
||||
await Promise.all(
|
||||
model.associations.map(async association => {
|
||||
if (!association.via || !entry._id || association.dominant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const search =
|
||||
_.endsWith(association.nature, 'One') || association.nature === 'oneToMany'
|
||||
? { [association.via]: entry._id }
|
||||
: { [association.via]: { $in: [entry._id] } };
|
||||
const update =
|
||||
_.endsWith(association.nature, 'One') || association.nature === 'oneToMany'
|
||||
? { [association.via]: null }
|
||||
: { $pull: { [association.via]: entry._id } };
|
||||
|
||||
// Retrieve model.
|
||||
const model = association.plugin
|
||||
? strapi.plugins[association.plugin].models[association.model || association.collection]
|
||||
: strapi.models[association.model || association.collection];
|
||||
|
||||
return model.updateMany(search, update);
|
||||
})
|
||||
);
|
||||
await model.deleteRelations(entry);
|
||||
|
||||
return entry.toObject ? entry.toObject() : null;
|
||||
}
|
||||
|
||||
@ -19,372 +19,39 @@ const getModel = function(model, plugin) {
|
||||
);
|
||||
};
|
||||
|
||||
const removeUndefinedKeys = obj => _.pickBy(obj, _.negate(_.isUndefined));
|
||||
|
||||
module.exports = {
|
||||
update: async function(params) {
|
||||
const relationUpdates = [];
|
||||
const populate = this.associations.map(x => x.alias);
|
||||
const primaryKeyValue = getValuePrimaryKey(params, this.primaryKey);
|
||||
|
||||
const entry = await this.findOne({ [this.primaryKey]: primaryKeyValue })
|
||||
.populate(populate)
|
||||
.lean();
|
||||
|
||||
// Only update fields which are on this document.
|
||||
const values =
|
||||
params.parseRelationships === false
|
||||
? params.values
|
||||
: Object.keys(removeUndefinedKeys(params.values)).reduce(
|
||||
(acc, attribute) => {
|
||||
const currentValue = entry[attribute];
|
||||
const newValue = params.values[attribute];
|
||||
|
||||
const association = this.associations.find(
|
||||
x => x.alias === attribute
|
||||
);
|
||||
|
||||
const details = this._attributes[attribute];
|
||||
|
||||
// set simple attributes
|
||||
if (!association && _.get(details, 'isVirtual') !== true) {
|
||||
return _.set(acc, attribute, newValue);
|
||||
}
|
||||
|
||||
const assocModel = getModel(
|
||||
details.model || details.collection,
|
||||
details.plugin
|
||||
);
|
||||
|
||||
switch (association.nature) {
|
||||
case 'oneWay': {
|
||||
return _.set(
|
||||
acc,
|
||||
attribute,
|
||||
_.get(newValue, assocModel.primaryKey, newValue)
|
||||
);
|
||||
}
|
||||
case 'oneToOne': {
|
||||
// if value is the same don't do anything
|
||||
if (currentValue === newValue) return acc;
|
||||
|
||||
// if the value is null, set field to null on both sides
|
||||
if (_.isNull(newValue)) {
|
||||
const updatePromise = assocModel.updateOne(
|
||||
{
|
||||
[assocModel.primaryKey]: getValuePrimaryKey(
|
||||
currentValue,
|
||||
assocModel.primaryKey
|
||||
),
|
||||
},
|
||||
{ [details.via]: null }
|
||||
);
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return _.set(acc, attribute, null);
|
||||
}
|
||||
|
||||
// set old relations to null
|
||||
const updateLink = this.updateOne(
|
||||
{ [attribute]: new mongoose.Types.ObjectId(newValue) },
|
||||
{ [attribute]: null }
|
||||
).then(() => {
|
||||
return assocModel.updateOne(
|
||||
{
|
||||
[this.primaryKey]: new mongoose.Types.ObjectId(
|
||||
newValue
|
||||
),
|
||||
},
|
||||
{ [details.via]: primaryKeyValue }
|
||||
);
|
||||
});
|
||||
|
||||
// set new relation
|
||||
relationUpdates.push(updateLink);
|
||||
return _.set(acc, attribute, newValue);
|
||||
}
|
||||
case 'oneToMany': {
|
||||
// set relation to null for all the ids not in the list
|
||||
const attributeIds = currentValue;
|
||||
const toRemove = _.differenceWith(
|
||||
attributeIds,
|
||||
newValue,
|
||||
(a, b) => {
|
||||
return (
|
||||
`${a[assocModel.primaryKey] || a}` ===
|
||||
`${b[assocModel.primaryKey] || b}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const updatePromise = assocModel
|
||||
.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: toRemove.map(
|
||||
val =>
|
||||
new mongoose.Types.ObjectId(
|
||||
val[assocModel.primaryKey] || val
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
{ [details.via]: null }
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: newValue.map(
|
||||
val =>
|
||||
new mongoose.Types.ObjectId(
|
||||
val[assocModel.primaryKey] || val
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
{ [details.via]: primaryKeyValue }
|
||||
);
|
||||
});
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return acc;
|
||||
}
|
||||
case 'manyToOne': {
|
||||
return _.set(
|
||||
acc,
|
||||
attribute,
|
||||
_.get(newValue, assocModel.primaryKey, newValue)
|
||||
);
|
||||
}
|
||||
case 'manyWay':
|
||||
case 'manyToMany': {
|
||||
if (association.dominant) {
|
||||
return _.set(
|
||||
acc,
|
||||
attribute,
|
||||
newValue
|
||||
? newValue.map(val => val[assocModel.primaryKey] || val)
|
||||
: newValue
|
||||
);
|
||||
}
|
||||
|
||||
const updatePomise = assocModel
|
||||
.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: currentValue.map(
|
||||
val =>
|
||||
new mongoose.Types.ObjectId(
|
||||
val[assocModel.primaryKey] || val
|
||||
)
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
$pull: {
|
||||
[association.via]: new mongoose.Types.ObjectId(
|
||||
primaryKeyValue
|
||||
),
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: newValue
|
||||
? newValue.map(
|
||||
val =>
|
||||
new mongoose.Types.ObjectId(
|
||||
val[assocModel.primaryKey] || val
|
||||
)
|
||||
)
|
||||
: newValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
$addToSet: { [association.via]: [primaryKeyValue] },
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
relationUpdates.push(updatePomise);
|
||||
return acc;
|
||||
}
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne': {
|
||||
// Update the relational array.
|
||||
|
||||
newValue.forEach(obj => {
|
||||
const refModel = strapi.getModel(obj.ref, obj.source);
|
||||
|
||||
const createRelation = () => {
|
||||
return module.exports.addRelationMorph.call(this, {
|
||||
id: entry[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: obj.kind || refModel.globalId,
|
||||
refId: new mongoose.Types.ObjectId(obj.refId),
|
||||
field: obj.field,
|
||||
filter: association.filter,
|
||||
});
|
||||
};
|
||||
|
||||
// Clear relations to refModel
|
||||
const reverseAssoc = refModel.associations.find(
|
||||
assoc => assoc.alias === obj.field
|
||||
);
|
||||
if (
|
||||
reverseAssoc &&
|
||||
reverseAssoc.nature === 'oneToManyMorph'
|
||||
) {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph
|
||||
.call(this, {
|
||||
alias: association.alias,
|
||||
ref: obj.kind || refModel.globalId,
|
||||
refId: new mongoose.Types.ObjectId(obj.refId),
|
||||
field: obj.field,
|
||||
filter: association.filter,
|
||||
})
|
||||
.then(createRelation)
|
||||
);
|
||||
} else {
|
||||
relationUpdates.push(createRelation());
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'oneToManyMorph':
|
||||
case 'manyToManyMorph': {
|
||||
const transformToArrayID = array => {
|
||||
const transformToArrayID = (array, pk) => {
|
||||
if (_.isArray(array)) {
|
||||
return array.map(value => {
|
||||
if (_.isPlainObject(value)) {
|
||||
return getValuePrimaryKey(value, this.primaryKey);
|
||||
return array
|
||||
.map(value => value && (getValuePrimaryKey(value, pk) || value))
|
||||
.filter(n => n)
|
||||
.map(val => _.toString(val));
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
return transformToArrayID([array]);
|
||||
};
|
||||
|
||||
if (
|
||||
association.type === 'model' ||
|
||||
(association.type === 'collection' && _.isObject(array))
|
||||
) {
|
||||
return _.isEmpty(array)
|
||||
? []
|
||||
: transformToArrayID([array]);
|
||||
}
|
||||
const removeUndefinedKeys = (obj = {}) => _.pickBy(obj, _.negate(_.isUndefined));
|
||||
|
||||
return [];
|
||||
};
|
||||
const addRelationMorph = async (model, params) => {
|
||||
const { id, alias, refId, ref, field, filter } = params;
|
||||
|
||||
// Compare array of ID to find deleted files.
|
||||
const attributeValue = transformToArrayID(currentValue).map(
|
||||
id => id.toString()
|
||||
);
|
||||
const storedValue = transformToArrayID(newValue).map(id =>
|
||||
id.toString()
|
||||
);
|
||||
|
||||
const toAdd = _.difference(storedValue, attributeValue);
|
||||
const toRemove = _.difference(attributeValue, storedValue);
|
||||
|
||||
const model = getModel(
|
||||
details.model || details.collection,
|
||||
details.plugin
|
||||
);
|
||||
|
||||
// Remove relations in the other side.
|
||||
toAdd.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.addRelationMorph.call(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.globalId,
|
||||
refId: entry._id,
|
||||
field: association.alias,
|
||||
filter: association.filter,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Remove relations in the other side.
|
||||
toRemove.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph.call(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.globalId,
|
||||
refId: entry._id,
|
||||
field: association.alias,
|
||||
})
|
||||
);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'oneMorphToOne':
|
||||
case 'oneMorphToMany':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return acc;
|
||||
await model.updateMany(
|
||||
{
|
||||
[model.primaryKey]: id,
|
||||
},
|
||||
{
|
||||
$push: {
|
||||
[alias]: {
|
||||
ref: new mongoose.Types.ObjectId(refId),
|
||||
kind: ref,
|
||||
[filter]: field,
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// Update virtuals fields.
|
||||
await Promise.all(relationUpdates).then(() =>
|
||||
this.updateOne({ [this.primaryKey]: primaryKeyValue }, values, {
|
||||
strict: false,
|
||||
})
|
||||
);
|
||||
|
||||
const updatedEntity = await this.findOne({
|
||||
[this.primaryKey]: primaryKeyValue,
|
||||
}).populate(populate);
|
||||
|
||||
return updatedEntity && updatedEntity.toObject
|
||||
? updatedEntity.toObject()
|
||||
: updatedEntity;
|
||||
},
|
||||
|
||||
async addRelationMorph(params) {
|
||||
const { alias, id } = params;
|
||||
|
||||
let entry = await this.findOne({
|
||||
[this.primaryKey]: id,
|
||||
});
|
||||
|
||||
if (!entry) return Promise.resolve();
|
||||
|
||||
// if association already exists ignore
|
||||
const relationExists = entry[alias].find(obj => {
|
||||
if (
|
||||
obj.kind === params.ref &&
|
||||
obj.ref.toString() === params.refId.toString() &&
|
||||
obj.field === params.field
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (relationExists) return Promise.resolve();
|
||||
|
||||
entry[alias].push({
|
||||
ref: new mongoose.Types.ObjectId(params.refId),
|
||||
kind: params.ref,
|
||||
[params.filter]: params.field,
|
||||
});
|
||||
|
||||
await entry.save();
|
||||
},
|
||||
|
||||
async removeRelationMorph(params) {
|
||||
const removeRelationMorph = async (model, params) => {
|
||||
const { alias } = params;
|
||||
|
||||
let opts;
|
||||
@ -405,24 +72,403 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
|
||||
const entries = await this.find(opts);
|
||||
await model.updateMany(opts, {
|
||||
$pull: {
|
||||
[alias]: {
|
||||
ref: params.refId,
|
||||
kind: params.ref,
|
||||
[params.filter]: params.field,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const updates = entries.map(entry => {
|
||||
entry[alias] = entry[alias].filter(obj => {
|
||||
if (
|
||||
obj.kind === params.ref &&
|
||||
obj.ref.toString() === params.refId.toString() &&
|
||||
obj.field === params.field
|
||||
) {
|
||||
return false;
|
||||
module.exports = {
|
||||
async update(params) {
|
||||
const relationUpdates = [];
|
||||
const populate = this.associations.map(x => x.alias);
|
||||
const primaryKeyValue = getValuePrimaryKey(params, this.primaryKey);
|
||||
|
||||
const entry = await this.findOne({ [this.primaryKey]: primaryKeyValue })
|
||||
.populate(populate)
|
||||
.lean();
|
||||
|
||||
// Only update fields which are on this document.
|
||||
const values = Object.keys(removeUndefinedKeys(params.values)).reduce((acc, attribute) => {
|
||||
const currentValue = entry[attribute];
|
||||
const newValue = params.values[attribute];
|
||||
|
||||
const association = this.associations.find(x => x.alias === attribute);
|
||||
|
||||
const details = this._attributes[attribute];
|
||||
|
||||
// set simple attributes
|
||||
if (!association && _.get(details, 'isVirtual') !== true) {
|
||||
return _.set(acc, attribute, newValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
const assocModel = getModel(details.model || details.collection, details.plugin);
|
||||
|
||||
switch (association.nature) {
|
||||
case 'oneWay': {
|
||||
return _.set(acc, attribute, _.get(newValue, assocModel.primaryKey, newValue));
|
||||
}
|
||||
case 'oneToOne': {
|
||||
// if value is the same don't do anything
|
||||
if (currentValue === newValue) return acc;
|
||||
|
||||
// if the value is null, set field to null on both sides
|
||||
if (_.isNull(newValue)) {
|
||||
const updatePromise = assocModel.updateOne(
|
||||
{
|
||||
[assocModel.primaryKey]: getValuePrimaryKey(currentValue, assocModel.primaryKey),
|
||||
},
|
||||
{ [details.via]: null }
|
||||
);
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return _.set(acc, attribute, null);
|
||||
}
|
||||
|
||||
// set old relations to null
|
||||
const updateLink = this.updateOne(
|
||||
{ [attribute]: new mongoose.Types.ObjectId(newValue) },
|
||||
{ [attribute]: null }
|
||||
).then(() => {
|
||||
return assocModel.updateOne(
|
||||
{
|
||||
[this.primaryKey]: new mongoose.Types.ObjectId(newValue),
|
||||
},
|
||||
{ [details.via]: primaryKeyValue }
|
||||
);
|
||||
});
|
||||
|
||||
return entry.save();
|
||||
// set new relation
|
||||
relationUpdates.push(updateLink);
|
||||
return _.set(acc, attribute, newValue);
|
||||
}
|
||||
case 'oneToMany': {
|
||||
// set relation to null for all the ids not in the list
|
||||
const attributeIds = currentValue;
|
||||
const toRemove = _.differenceWith(attributeIds, newValue, (a, b) => {
|
||||
return `${a[assocModel.primaryKey] || a}` === `${b[assocModel.primaryKey] || b}`;
|
||||
});
|
||||
|
||||
await Promise.all(updates);
|
||||
const updatePromise = assocModel
|
||||
.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: toRemove.map(
|
||||
val => new mongoose.Types.ObjectId(val[assocModel.primaryKey] || val)
|
||||
),
|
||||
},
|
||||
},
|
||||
{ [details.via]: null }
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: newValue.map(
|
||||
val => new mongoose.Types.ObjectId(val[assocModel.primaryKey] || val)
|
||||
),
|
||||
},
|
||||
},
|
||||
{ [details.via]: primaryKeyValue }
|
||||
);
|
||||
});
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return acc;
|
||||
}
|
||||
case 'manyToOne': {
|
||||
return _.set(acc, attribute, _.get(newValue, assocModel.primaryKey, newValue));
|
||||
}
|
||||
case 'manyWay':
|
||||
case 'manyToMany': {
|
||||
if (association.dominant) {
|
||||
return _.set(
|
||||
acc,
|
||||
attribute,
|
||||
newValue ? newValue.map(val => val[assocModel.primaryKey] || val) : newValue
|
||||
);
|
||||
}
|
||||
|
||||
const updatePomise = assocModel
|
||||
.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: currentValue.map(
|
||||
val => new mongoose.Types.ObjectId(val[assocModel.primaryKey] || val)
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
$pull: {
|
||||
[association.via]: new mongoose.Types.ObjectId(primaryKeyValue),
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel.updateMany(
|
||||
{
|
||||
[assocModel.primaryKey]: {
|
||||
$in: newValue
|
||||
? newValue.map(
|
||||
val => new mongoose.Types.ObjectId(val[assocModel.primaryKey] || val)
|
||||
)
|
||||
: newValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
$addToSet: { [association.via]: [primaryKeyValue] },
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
relationUpdates.push(updatePomise);
|
||||
return acc;
|
||||
}
|
||||
// media -> model
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne': {
|
||||
newValue.forEach(obj => {
|
||||
const refModel = strapi.getModel(obj.ref, obj.source);
|
||||
|
||||
const createRelation = () => {
|
||||
return addRelationMorph(this, {
|
||||
id: entry[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: obj.kind || refModel.globalId,
|
||||
refId: new mongoose.Types.ObjectId(obj.refId),
|
||||
field: obj.field,
|
||||
filter: association.filter,
|
||||
});
|
||||
};
|
||||
|
||||
// Clear relations to refModel
|
||||
const reverseAssoc = refModel.associations.find(assoc => assoc.alias === obj.field);
|
||||
if (reverseAssoc && reverseAssoc.nature === 'oneToManyMorph') {
|
||||
relationUpdates.push(
|
||||
removeRelationMorph(this, {
|
||||
alias: association.alias,
|
||||
ref: obj.kind || refModel.globalId,
|
||||
refId: new mongoose.Types.ObjectId(obj.refId),
|
||||
field: obj.field,
|
||||
filter: association.filter,
|
||||
})
|
||||
.then(createRelation)
|
||||
.then(() => {
|
||||
// set field inside refModel
|
||||
return refModel.updateMany(
|
||||
{
|
||||
[refModel.primaryKey]: new mongoose.Types.ObjectId(obj.refId),
|
||||
},
|
||||
{
|
||||
[obj.field]: new mongoose.Types.ObjectId(entry[this.primaryKey]),
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
relationUpdates.push(
|
||||
createRelation().then(() => {
|
||||
// push to field inside refModel
|
||||
return refModel.updateMany(
|
||||
{
|
||||
[refModel.primaryKey]: new mongoose.Types.ObjectId(obj.refId),
|
||||
},
|
||||
{
|
||||
$push: { [obj.field]: new mongoose.Types.ObjectId(entry[this.primaryKey]) },
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
// model -> media
|
||||
case 'oneToManyMorph':
|
||||
case 'manyToManyMorph': {
|
||||
// Compare array of ID to find deleted files.
|
||||
const currentIds = transformToArrayID(currentValue, this.primaryKey);
|
||||
const newIds = transformToArrayID(newValue, this.primaryKey);
|
||||
|
||||
const toAdd = _.difference(newIds, currentIds);
|
||||
const toRemove = _.difference(currentIds, newIds);
|
||||
|
||||
const model = getModel(details.model || details.collection, details.plugin);
|
||||
|
||||
_.set(acc, attribute, newIds);
|
||||
|
||||
const addPromise = Promise.all(
|
||||
toAdd.map(id => {
|
||||
return addRelationMorph(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.globalId,
|
||||
refId: entry._id,
|
||||
field: association.alias,
|
||||
filter: association.filter,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
relationUpdates.push(addPromise);
|
||||
|
||||
toRemove.forEach(id => {
|
||||
relationUpdates.push(
|
||||
removeRelationMorph(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.globalId,
|
||||
refId: entry._id,
|
||||
field: association.alias,
|
||||
filter: association.filter,
|
||||
})
|
||||
);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'oneMorphToOne':
|
||||
case 'oneMorphToMany':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Update virtuals fields.
|
||||
await Promise.all(relationUpdates).then(() =>
|
||||
this.updateOne({ [this.primaryKey]: primaryKeyValue }, values, {
|
||||
strict: false,
|
||||
})
|
||||
);
|
||||
|
||||
const updatedEntity = await this.findOne({
|
||||
[this.primaryKey]: primaryKeyValue,
|
||||
}).populate(populate);
|
||||
|
||||
return updatedEntity && updatedEntity.toObject ? updatedEntity.toObject() : updatedEntity;
|
||||
},
|
||||
|
||||
deleteRelations(entry) {
|
||||
const primaryKeyValue = entry[this.primaryKey];
|
||||
|
||||
return Promise.all(
|
||||
this.associations.map(async association => {
|
||||
const { nature, via, dominant } = association;
|
||||
|
||||
// TODO: delete all the ref to the model
|
||||
|
||||
switch (nature) {
|
||||
case 'oneWay':
|
||||
case 'manyWay': {
|
||||
return;
|
||||
}
|
||||
case 'oneToMany':
|
||||
case 'oneToOne': {
|
||||
if (!via) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetModel = strapi.db.getModel(
|
||||
association.model || association.collection,
|
||||
association.plugin
|
||||
);
|
||||
|
||||
return targetModel.updateMany({ [via]: primaryKeyValue }, { [via]: null });
|
||||
}
|
||||
case 'manyToMany':
|
||||
case 'manyToOne': {
|
||||
if (!via || dominant) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetModel = strapi.db.getModel(
|
||||
association.model || association.collection,
|
||||
association.plugin
|
||||
);
|
||||
|
||||
return targetModel.updateMany(
|
||||
{ [via]: primaryKeyValue },
|
||||
{ $pull: { [via]: primaryKeyValue } }
|
||||
);
|
||||
}
|
||||
case 'oneToManyMorph':
|
||||
case 'manyToManyMorph': {
|
||||
// delete relation inside of the ref model
|
||||
|
||||
const targetModel = strapi.db.getModel(
|
||||
association.model || association.collection,
|
||||
association.plugin
|
||||
);
|
||||
|
||||
// ignore them ghost relations
|
||||
if (!targetModel) return;
|
||||
|
||||
const element = {
|
||||
ref: primaryKeyValue,
|
||||
kind: this.globalId,
|
||||
[association.filter]: association.alias,
|
||||
};
|
||||
|
||||
return targetModel.updateMany(
|
||||
{ [via]: { $elemMatch: element } },
|
||||
{ $pull: { [via]: element } }
|
||||
);
|
||||
}
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne': {
|
||||
// delete relation inside of the ref model
|
||||
// console.log(entry[association.alias]);
|
||||
|
||||
if (Array.isArray(entry[association.alias])) {
|
||||
return Promise.all(
|
||||
entry[association.alias].map(val => {
|
||||
const targetModel = strapi.db.getModelByGlobalId(val.kind);
|
||||
|
||||
// ignore them ghost relations
|
||||
if (!targetModel) return;
|
||||
|
||||
const field = val[association.filter];
|
||||
const reverseAssoc = targetModel.associations.find(
|
||||
assoc => assoc.alias === field
|
||||
);
|
||||
|
||||
if (reverseAssoc && reverseAssoc.nature === 'oneToManyMorph') {
|
||||
return targetModel.updateMany(
|
||||
{
|
||||
[targetModel.primaryKey]: val.ref && (val.ref._id || val.ref),
|
||||
},
|
||||
{
|
||||
[field]: null,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return targetModel.updateMany(
|
||||
{
|
||||
[targetModel.primaryKey]: val.ref && (val.ref._id || val.ref),
|
||||
},
|
||||
{
|
||||
$pull: { [field]: primaryKeyValue },
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 'oneMorphToOne':
|
||||
case 'oneMorphToMany': {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@ -117,6 +117,12 @@ class DatabaseManager {
|
||||
return model.collectionName === collectionName;
|
||||
});
|
||||
}
|
||||
|
||||
getModelByGlobalId(globalId) {
|
||||
return Array.from(this.models.values()).find(model => {
|
||||
return model.globalId === globalId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createDatabaseManager(strapi) {
|
||||
|
||||
@ -269,7 +269,7 @@ module.exports = {
|
||||
const { id, model, field } = params;
|
||||
|
||||
const arr = Array.isArray(files) ? files : [files];
|
||||
return Promise.all(
|
||||
const enhancedFiles = await Promise.all(
|
||||
arr.map(file => {
|
||||
return this.enhanceFile(
|
||||
file,
|
||||
@ -282,7 +282,9 @@ module.exports = {
|
||||
}
|
||||
);
|
||||
})
|
||||
).then(files => this.uploadFileAndPersist(files));
|
||||
);
|
||||
|
||||
await Promise.all(enhancedFiles.map(file => this.uploadFileAndPersist(file)));
|
||||
},
|
||||
|
||||
getSettings() {
|
||||
|
||||
68
packages/strapi-utils/lib/__tests__/models.test.js
Normal file
68
packages/strapi-utils/lib/__tests__/models.test.js
Normal file
@ -0,0 +1,68 @@
|
||||
const { getNature } = require('../models');
|
||||
|
||||
describe('getNature', () => {
|
||||
describe('oneWay', () => {
|
||||
test('oneWay', () => {
|
||||
global.strapi = {
|
||||
models: {
|
||||
baseModel: {
|
||||
attributes: {
|
||||
test: {
|
||||
model: 'modelName',
|
||||
},
|
||||
},
|
||||
},
|
||||
modelName: {},
|
||||
},
|
||||
plugins: {},
|
||||
};
|
||||
|
||||
const nature = getNature({
|
||||
attribute: global.strapi.models.baseModel.attributes.test,
|
||||
attributeName: 'test',
|
||||
modelName: 'baseModel',
|
||||
});
|
||||
|
||||
expect(nature).toEqual({
|
||||
nature: 'oneWay',
|
||||
verbose: 'belongsTo',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('oneToOne', () => {
|
||||
test('oneToOne', () => {
|
||||
global.strapi = {
|
||||
models: {
|
||||
baseModel: {
|
||||
attributes: {
|
||||
test: {
|
||||
model: 'modelName',
|
||||
via: 'reverseAttribute',
|
||||
},
|
||||
},
|
||||
},
|
||||
modelName: {
|
||||
attributes: {
|
||||
reverseAttribute: {
|
||||
model: 'baseModel',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {},
|
||||
};
|
||||
|
||||
const nature = getNature({
|
||||
attribute: global.strapi.models.baseModel.attributes.test,
|
||||
attributeName: 'test',
|
||||
modelName: 'baseModel',
|
||||
});
|
||||
|
||||
expect(nature).toEqual({
|
||||
nature: 'oneToOne',
|
||||
verbose: 'belongsTo',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -13,7 +13,6 @@ const isNumeric = value => {
|
||||
return !_.isObject(value) && !isNaN(parseFloat(value)) && isFinite(value);
|
||||
};
|
||||
|
||||
/* eslint-disable prefer-template */
|
||||
/*
|
||||
* Set of utils for models
|
||||
*/
|
||||
@ -38,58 +37,41 @@ module.exports = {
|
||||
* Find relation nature with verbose
|
||||
*/
|
||||
|
||||
getNature: (association, key, models, currentModelName) => {
|
||||
try {
|
||||
getNature: ({ attribute, attributeName, modelName }) => {
|
||||
const types = {
|
||||
current: '',
|
||||
other: '',
|
||||
};
|
||||
|
||||
if (_.isUndefined(models)) {
|
||||
models = association.plugin
|
||||
? strapi.plugins[association.plugin].models
|
||||
: strapi.models;
|
||||
}
|
||||
const models = attribute.plugin ? strapi.plugins[attribute.plugin].models : strapi.models;
|
||||
|
||||
const pluginModels = Object.values(strapi.plugins).reduce((acc, plugin) => {
|
||||
return acc.concat(Object.values(plugin.models));
|
||||
}, []);
|
||||
|
||||
const allModels = Object.values(strapi.models).concat(pluginModels);
|
||||
|
||||
if (
|
||||
(_.has(association, 'collection') && association.collection === '*') ||
|
||||
(_.has(association, 'model') && association.model === '*')
|
||||
(_.has(attribute, 'collection') && attribute.collection === '*') ||
|
||||
(_.has(attribute, 'model') && attribute.model === '*')
|
||||
) {
|
||||
if (association.model) {
|
||||
if (attribute.model) {
|
||||
types.current = 'morphToD';
|
||||
} else {
|
||||
types.current = 'morphTo';
|
||||
}
|
||||
|
||||
const flattenedPluginsModels = Object.keys(strapi.plugins).reduce(
|
||||
(acc, current) => {
|
||||
Object.keys(strapi.plugins[current].models).forEach(model => {
|
||||
acc[`${current}_${model}`] =
|
||||
strapi.plugins[current].models[model];
|
||||
});
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const allModels = _.merge({}, strapi.models, flattenedPluginsModels);
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(allModels, model => {
|
||||
_.forEach(allModels, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (
|
||||
_.has(attribute, 'via') &&
|
||||
attribute.via === key &&
|
||||
attribute.model === currentModelName
|
||||
) {
|
||||
if (_.has(attribute, 'collection')) {
|
||||
if (_.has(attribute, 'via') && attribute.via === attributeName) {
|
||||
if (_.has(attribute, 'collection') && attribute.collection === modelName) {
|
||||
types.other = 'collection';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (_.has(attribute, 'model')) {
|
||||
types.other = 'model';
|
||||
} else if (_.has(attribute, 'model') && attribute.model === modelName) {
|
||||
types.other = 'modelD';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
@ -97,22 +79,14 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (
|
||||
_.has(association, 'via') &&
|
||||
_.has(association, 'collection')
|
||||
) {
|
||||
const relatedAttribute =
|
||||
models[association.collection].attributes[association.via];
|
||||
} else if (_.has(attribute, 'via') && _.has(attribute, 'collection')) {
|
||||
const relatedAttribute = models[attribute.collection].attributes[attribute.via];
|
||||
|
||||
if (!relatedAttribute) {
|
||||
throw new Error(
|
||||
`The attribute \`${
|
||||
association.via
|
||||
}\` is missing in the model ${_.upperFirst(
|
||||
association.collection
|
||||
)} ${
|
||||
association.plugin ? '(plugin - ' + association.plugin + ')' : ''
|
||||
}`
|
||||
`The attribute \`${attribute.via}\` is missing in the model ${_.upperFirst(
|
||||
attribute.collection
|
||||
)} ${attribute.plugin ? '(plugin - ' + attribute.plugin + ')' : ''}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -130,59 +104,44 @@ module.exports = {
|
||||
!_.has(relatedAttribute, 'via')
|
||||
) {
|
||||
types.other = 'collectionD';
|
||||
} else if (
|
||||
_.has(relatedAttribute, 'model') &&
|
||||
relatedAttribute.model !== '*'
|
||||
) {
|
||||
} else if (_.has(relatedAttribute, 'model') && relatedAttribute.model !== '*') {
|
||||
types.other = 'model';
|
||||
} else if (
|
||||
_.has(relatedAttribute, 'collection') ||
|
||||
_.has(relatedAttribute, 'model')
|
||||
) {
|
||||
} else if (_.has(relatedAttribute, 'collection') || _.has(relatedAttribute, 'model')) {
|
||||
types.other = 'morphTo';
|
||||
}
|
||||
} else if (_.has(association, 'via') && _.has(association, 'model')) {
|
||||
} else if (_.has(attribute, 'via') && _.has(attribute, 'model')) {
|
||||
types.current = 'modelD';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
const model = models[association.model];
|
||||
// We have to find if they are a model linked to this attributeName
|
||||
const model = models[attribute.model];
|
||||
|
||||
const attribute = model.attributes[association.via];
|
||||
const reverseAttribute = model.attributes[attribute.via];
|
||||
|
||||
if (
|
||||
_.has(attribute, 'via') &&
|
||||
attribute.via === key &&
|
||||
_.has(attribute, 'collection') &&
|
||||
attribute.collection !== '*'
|
||||
_.has(reverseAttribute, 'via') &&
|
||||
reverseAttribute.via === attributeName &&
|
||||
_.has(reverseAttribute, 'collection') &&
|
||||
reverseAttribute.collection !== '*'
|
||||
) {
|
||||
types.other = 'collection';
|
||||
} else if (_.has(attribute, 'model') && attribute.model !== '*') {
|
||||
} else if (_.has(reverseAttribute, 'model') && reverseAttribute.model !== '*') {
|
||||
types.other = 'model';
|
||||
} else if (
|
||||
_.has(attribute, 'collection') ||
|
||||
_.has(attribute, 'model')
|
||||
) {
|
||||
} else if (_.has(reverseAttribute, 'collection') || _.has(reverseAttribute, 'model')) {
|
||||
types.other = 'morphTo';
|
||||
}
|
||||
} else if (_.has(association, 'model')) {
|
||||
} else if (_.has(attribute, 'model')) {
|
||||
types.current = 'model';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
// We have to find if they are a model linked to this attributeName
|
||||
_.forIn(models, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (_.has(attribute, 'via') && attribute.via === key) {
|
||||
if (
|
||||
_.has(attribute, 'collection') &&
|
||||
attribute.collection === currentModelName
|
||||
) {
|
||||
if (_.has(attribute, 'via') && attribute.via === attributeName) {
|
||||
if (_.has(attribute, 'collection') && attribute.collection === modelName) {
|
||||
types.other = 'collection';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (
|
||||
_.has(attribute, 'model') &&
|
||||
attribute.model === currentModelName
|
||||
) {
|
||||
} else if (_.has(attribute, 'model') && attribute.model === modelName) {
|
||||
types.other = 'modelD';
|
||||
|
||||
// Break loop
|
||||
@ -191,25 +150,19 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (_.has(association, 'collection')) {
|
||||
} else if (_.has(attribute, 'collection')) {
|
||||
types.current = 'collectionD';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
// We have to find if they are a model linked to this attributeName
|
||||
_.forIn(models, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (_.has(attribute, 'via') && attribute.via === key) {
|
||||
if (
|
||||
_.has(attribute, 'collection') &&
|
||||
attribute.collection === currentModelName
|
||||
) {
|
||||
if (_.has(attribute, 'via') && attribute.via === attributeName) {
|
||||
if (_.has(attribute, 'collection') && attribute.collection === modelName) {
|
||||
types.other = 'collection';
|
||||
|
||||
// Break loop
|
||||
return false;
|
||||
} else if (
|
||||
_.has(attribute, 'model') &&
|
||||
attribute.model === currentModelName
|
||||
) {
|
||||
} else if (_.has(attribute, 'model') && attribute.model === modelName) {
|
||||
types.other = 'modelD';
|
||||
|
||||
// Break loop
|
||||
@ -252,7 +205,7 @@ module.exports = {
|
||||
};
|
||||
} else if (
|
||||
types.current === 'morphTo' &&
|
||||
(types.other === 'model' || _.has(association, 'model'))
|
||||
(types.other === 'model' || _.has(attribute, 'model'))
|
||||
) {
|
||||
return {
|
||||
nature: 'manyMorphToOne',
|
||||
@ -260,7 +213,7 @@ module.exports = {
|
||||
};
|
||||
} else if (
|
||||
types.current === 'morphTo' &&
|
||||
(types.other === 'collection' || _.has(association, 'collection'))
|
||||
(types.other === 'collection' || _.has(attribute, 'collection'))
|
||||
) {
|
||||
return {
|
||||
nature: 'manyMorphToMany',
|
||||
@ -294,10 +247,7 @@ module.exports = {
|
||||
nature: 'oneToMany',
|
||||
verbose: 'hasMany',
|
||||
};
|
||||
} else if (
|
||||
types.current === 'collection' &&
|
||||
types.other === 'collection'
|
||||
) {
|
||||
} else if (types.current === 'collection' && types.other === 'collection') {
|
||||
return {
|
||||
nature: 'manyToMany',
|
||||
verbose: 'belongsToMany',
|
||||
@ -323,15 +273,6 @@ module.exports = {
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} catch (e) {
|
||||
strapi.log.error(
|
||||
`Something went wrong in the model \`${_.upperFirst(
|
||||
currentModelName
|
||||
)}\` with the attribute \`${key}\``
|
||||
);
|
||||
strapi.log.error(e);
|
||||
strapi.stop();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -347,9 +288,7 @@ module.exports = {
|
||||
return a.collection < b.collection ? -1 : 1;
|
||||
})
|
||||
.map(table =>
|
||||
_.snakeCase(
|
||||
`${pluralize.plural(table.collection)} ${pluralize.plural(table.via)}`
|
||||
)
|
||||
_.snakeCase(`${pluralize.plural(table.collection)} ${pluralize.plural(table.via)}`)
|
||||
)
|
||||
.join('__');
|
||||
},
|
||||
@ -373,32 +312,21 @@ module.exports = {
|
||||
// Get relation nature
|
||||
let details;
|
||||
const targetName = association.model || association.collection || '';
|
||||
const infos = this.getNature(
|
||||
association,
|
||||
key,
|
||||
undefined,
|
||||
model.toLowerCase()
|
||||
);
|
||||
const infos = this.getNature({
|
||||
attribute: association,
|
||||
attributeName: key,
|
||||
modelName: model.toLowerCase(),
|
||||
});
|
||||
|
||||
if (targetName !== '*') {
|
||||
if (association.plugin) {
|
||||
details = _.get(
|
||||
strapi.plugins,
|
||||
[
|
||||
association.plugin,
|
||||
'models',
|
||||
targetName,
|
||||
'attributes',
|
||||
association.via,
|
||||
],
|
||||
[association.plugin, 'models', targetName, 'attributes', association.via],
|
||||
{}
|
||||
);
|
||||
} else {
|
||||
details = _.get(
|
||||
strapi.models,
|
||||
[targetName, 'attributes', association.via],
|
||||
{}
|
||||
);
|
||||
details = _.get(strapi.models, [targetName, 'attributes', association.via], {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,14 +346,11 @@ module.exports = {
|
||||
|
||||
if (infos.nature === 'manyToMany' && definition.orm === 'bookshelf') {
|
||||
ast.tableCollectionName =
|
||||
_.get(association, 'collectionName') ||
|
||||
this.getCollectionName(details, association);
|
||||
_.get(association, 'collectionName') || this.getCollectionName(details, association);
|
||||
}
|
||||
|
||||
if (infos.nature === 'manyWay' && definition.orm === 'bookshelf') {
|
||||
ast.tableCollectionName = `${
|
||||
definition.collectionName
|
||||
}__${_.snakeCase(key)}`;
|
||||
ast.tableCollectionName = `${definition.collectionName}__${_.snakeCase(key)}`;
|
||||
}
|
||||
|
||||
definition.associations.push(ast);
|
||||
@ -447,37 +372,25 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
const pluginsModels = Object.keys(strapi.plugins).reduce(
|
||||
(acc, current) => {
|
||||
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];
|
||||
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()
|
||||
) {
|
||||
if ((attr.collection || attr.model || '').toLowerCase() === model.toLowerCase()) {
|
||||
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()
|
||||
) {
|
||||
if ((attr.collection || attr.model || '').toLowerCase() === model.toLowerCase()) {
|
||||
acc.push(strapi.models[entity].globalId);
|
||||
}
|
||||
});
|
||||
@ -485,29 +398,19 @@ module.exports = {
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const componentModels = Object.keys(strapi.components).reduce(
|
||||
(acc, entity) => {
|
||||
Object.keys(strapi.components[entity].attributes).forEach(
|
||||
attribute => {
|
||||
const componentModels = Object.keys(strapi.components).reduce((acc, entity) => {
|
||||
Object.keys(strapi.components[entity].attributes).forEach(attribute => {
|
||||
const attr = strapi.components[entity].attributes[attribute];
|
||||
|
||||
if (
|
||||
(attr.collection || attr.model || '').toLowerCase() ===
|
||||
model.toLowerCase()
|
||||
) {
|
||||
if ((attr.collection || attr.model || '').toLowerCase() === model.toLowerCase()) {
|
||||
acc.push(strapi.components[entity].globalId);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}, []);
|
||||
|
||||
const models = _.uniq(
|
||||
appModels.concat(pluginsModels).concat(componentModels)
|
||||
);
|
||||
const models = _.uniq(appModels.concat(pluginsModels).concat(componentModels));
|
||||
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
@ -519,9 +422,7 @@ module.exports = {
|
||||
});
|
||||
} catch (e) {
|
||||
strapi.log.error(
|
||||
`Something went wrong in the model \`${_.upperFirst(
|
||||
model
|
||||
)}\` with the attribute \`${key}\``
|
||||
`Something went wrong in the model \`${_.upperFirst(model)}\` with the attribute \`${key}\``
|
||||
);
|
||||
strapi.log.error(e);
|
||||
strapi.stop();
|
||||
@ -559,9 +460,7 @@ module.exports = {
|
||||
const connector = models[model].orm;
|
||||
|
||||
if (!connector) {
|
||||
throw new Error(
|
||||
`Impossible to determine the ORM used for the model ${model}.`
|
||||
);
|
||||
throw new Error(`Impossible to determine the ORM used for the model ${model}.`);
|
||||
}
|
||||
|
||||
const convertor = strapi.db.connectors.get(connector).getQueryParams;
|
||||
@ -614,17 +513,7 @@ module.exports = {
|
||||
|
||||
if (
|
||||
_.includes(
|
||||
[
|
||||
'ne',
|
||||
'lt',
|
||||
'gt',
|
||||
'lte',
|
||||
'gte',
|
||||
'contains',
|
||||
'containss',
|
||||
'in',
|
||||
'nin',
|
||||
],
|
||||
['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss', 'in', 'nin'],
|
||||
_.last(suffix)
|
||||
)
|
||||
) {
|
||||
|
||||
@ -289,7 +289,9 @@ class Strapi extends EventEmitter {
|
||||
|
||||
stop(exitCode = 1) {
|
||||
// Destroy server and available connections.
|
||||
if (_.has(this, 'server.destroy')) {
|
||||
this.server.destroy();
|
||||
}
|
||||
|
||||
if (this.config.autoReload) {
|
||||
process.send('stop');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user