2018-05-09 16:08:58 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module dependencies
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Public node modules.
|
|
|
|
const _ = require('lodash');
|
|
|
|
|
2018-05-11 11:47:27 +02:00
|
|
|
// Utils
|
2019-07-05 17:17:39 +02:00
|
|
|
const {
|
|
|
|
models: { getValuePrimaryKey },
|
|
|
|
} = require('strapi-utils');
|
2018-05-11 11:47:27 +02:00
|
|
|
|
2018-07-20 17:20:06 +02:00
|
|
|
const transformToArrayID = (array, association) => {
|
2019-07-05 17:17:39 +02:00
|
|
|
if (_.isArray(array)) {
|
2018-07-20 17:20:06 +02:00
|
|
|
array = array.map(value => {
|
2018-05-09 16:08:58 +02:00
|
|
|
if (_.isPlainObject(value)) {
|
2018-07-20 17:20:06 +02:00
|
|
|
return value._id || value.id || false;
|
2018-05-09 16:08:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
2018-05-16 12:07:02 +02:00
|
|
|
});
|
2018-07-20 17:20:06 +02:00
|
|
|
|
|
|
|
return array.filter(n => n);
|
2018-05-09 16:08:58 +02:00
|
|
|
}
|
|
|
|
|
2019-07-05 17:17:39 +02:00
|
|
|
if (
|
|
|
|
association.type === 'model' ||
|
|
|
|
(association.type === 'collection' && _.isObject(array))
|
|
|
|
) {
|
2018-05-09 16:08:58 +02:00
|
|
|
return _.isEmpty(_.toString(array)) ? [] : transformToArrayID([array]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
};
|
|
|
|
|
2019-03-28 12:13:32 +01:00
|
|
|
const getModel = (model, plugin) => {
|
2019-07-05 17:17:39 +02:00
|
|
|
return (
|
|
|
|
_.get(strapi.plugins, [plugin, 'models', model]) ||
|
|
|
|
_.get(strapi, ['models', model]) ||
|
|
|
|
undefined
|
|
|
|
);
|
2019-03-28 12:13:32 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const removeUndefinedKeys = obj => _.pickBy(obj, _.negate(_.isUndefined));
|
2018-05-09 16:08:58 +02:00
|
|
|
|
2019-03-28 12:13:32 +01:00
|
|
|
module.exports = {
|
2019-07-24 17:16:50 +02:00
|
|
|
async findOne(params, populate, { transacting } = {}) {
|
2019-07-05 17:17:39 +02:00
|
|
|
const record = await this.forge({
|
|
|
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
|
|
|
}).fetch({
|
2019-07-24 17:16:50 +02:00
|
|
|
transacting,
|
2019-07-30 16:50:11 +02:00
|
|
|
withRelated: populate,
|
2019-07-05 17:17:39 +02:00
|
|
|
});
|
2018-05-09 16:08:58 +02:00
|
|
|
|
|
|
|
const data = record ? record.toJSON() : record;
|
|
|
|
|
|
|
|
// Retrieve data manually.
|
|
|
|
if (_.isEmpty(populate)) {
|
|
|
|
const arrayOfPromises = this.associations
|
2019-07-05 17:17:39 +02:00
|
|
|
.filter(association =>
|
|
|
|
['manyMorphToOne', 'manyMorphToMany'].includes(association.nature)
|
|
|
|
)
|
2018-05-16 12:07:02 +02:00
|
|
|
.map(() => {
|
2019-07-05 17:17:39 +02:00
|
|
|
return this.morph
|
|
|
|
.forge()
|
2018-05-09 16:08:58 +02:00
|
|
|
.where({
|
2019-07-05 17:17:39 +02:00
|
|
|
[`${this.collectionName}_id`]: getValuePrimaryKey(
|
|
|
|
params,
|
|
|
|
this.primaryKey
|
|
|
|
),
|
2018-05-09 16:08:58 +02:00
|
|
|
})
|
2019-07-24 17:16:50 +02:00
|
|
|
.fetchAll({
|
|
|
|
transacting,
|
|
|
|
});
|
2018-05-09 16:08:58 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
const related = await Promise.all(arrayOfPromises);
|
|
|
|
|
|
|
|
related.forEach((value, index) => {
|
|
|
|
data[this.associations[index].alias] = value ? value.toJSON() : value;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
|
2019-07-24 17:16:50 +02:00
|
|
|
async update(params, { transacting } = {}) {
|
2019-03-28 12:13:32 +01:00
|
|
|
const relationUpdates = [];
|
|
|
|
const primaryKeyValue = getValuePrimaryKey(params, this.primaryKey);
|
2019-07-24 17:16:50 +02:00
|
|
|
const response = await module.exports.findOne.call(this, params, null, {
|
|
|
|
transacting,
|
|
|
|
});
|
2018-05-09 16:08:58 +02:00
|
|
|
|
|
|
|
// Only update fields which are on this document.
|
2019-07-05 17:17:39 +02:00
|
|
|
const values =
|
|
|
|
params.parseRelationships === false
|
|
|
|
? params.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 details = this._attributes[current];
|
|
|
|
|
|
|
|
if (!association && _.get(details, 'isVirtual') !== true) {
|
|
|
|
return _.set(acc, current, property);
|
|
|
|
}
|
|
|
|
|
|
|
|
const assocModel = getModel(
|
|
|
|
details.model || details.collection,
|
|
|
|
details.plugin
|
2018-05-09 16:08:58 +02:00
|
|
|
);
|
2019-07-05 17:17:39 +02:00
|
|
|
switch (association.nature) {
|
|
|
|
case 'oneWay': {
|
|
|
|
return _.set(
|
|
|
|
acc,
|
|
|
|
current,
|
|
|
|
_.get(property, assocModel.primaryKey, property)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
case 'oneToOne': {
|
|
|
|
if (response[current] === property) return acc;
|
|
|
|
|
|
|
|
if (_.isNull(property)) {
|
|
|
|
const updatePromise = assocModel
|
|
|
|
.where({
|
|
|
|
[assocModel.primaryKey]: getValuePrimaryKey(
|
|
|
|
response[current],
|
|
|
|
assocModel.primaryKey
|
|
|
|
),
|
|
|
|
})
|
|
|
|
.save(
|
|
|
|
{ [details.via]: null },
|
2019-07-24 17:16:50 +02:00
|
|
|
{
|
|
|
|
method: 'update',
|
|
|
|
patch: true,
|
|
|
|
require: false,
|
|
|
|
transacting,
|
|
|
|
}
|
2019-07-05 17:17:39 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
relationUpdates.push(updatePromise);
|
|
|
|
return _.set(acc, current, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set old relations to null
|
|
|
|
const updateLink = this.where({ [current]: property })
|
|
|
|
.save(
|
|
|
|
{ [current]: null },
|
2019-07-24 17:16:50 +02:00
|
|
|
{
|
|
|
|
method: 'update',
|
|
|
|
patch: true,
|
|
|
|
require: false,
|
|
|
|
transacting,
|
|
|
|
}
|
2019-07-05 17:17:39 +02:00
|
|
|
)
|
|
|
|
.then(() => {
|
|
|
|
return assocModel
|
|
|
|
.where({ [this.primaryKey]: property })
|
|
|
|
.save(
|
|
|
|
{ [details.via]: primaryKeyValue },
|
2019-07-24 17:16:50 +02:00
|
|
|
{
|
|
|
|
method: 'update',
|
|
|
|
patch: true,
|
|
|
|
require: false,
|
|
|
|
transacting,
|
|
|
|
}
|
2019-07-05 17:17:39 +02:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
// set new relation
|
|
|
|
relationUpdates.push(updateLink);
|
|
|
|
return _.set(acc, current, property);
|
|
|
|
}
|
|
|
|
case 'oneToMany': {
|
|
|
|
// receive array of ids or array of objects with ids
|
|
|
|
|
|
|
|
// 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 updatePromise = assocModel
|
|
|
|
.where(
|
|
|
|
assocModel.primaryKey,
|
|
|
|
'in',
|
|
|
|
toRemove.map(val => val[assocModel.primaryKey] || val)
|
|
|
|
)
|
|
|
|
.save(
|
|
|
|
{ [details.via]: null },
|
2019-07-24 17:16:50 +02:00
|
|
|
{
|
|
|
|
method: 'update',
|
|
|
|
patch: true,
|
|
|
|
require: false,
|
|
|
|
transacting,
|
|
|
|
}
|
2019-07-05 17:17:39 +02:00
|
|
|
)
|
|
|
|
.then(() => {
|
|
|
|
return assocModel
|
|
|
|
.where(
|
|
|
|
assocModel.primaryKey,
|
|
|
|
'in',
|
|
|
|
property.map(val => val[assocModel.primaryKey] || val)
|
|
|
|
)
|
|
|
|
.save(
|
|
|
|
{ [details.via]: primaryKeyValue },
|
2019-07-24 17:16:50 +02:00
|
|
|
{
|
|
|
|
method: 'update',
|
|
|
|
patch: true,
|
|
|
|
require: false,
|
|
|
|
transacting,
|
|
|
|
}
|
2019-07-05 17:17:39 +02:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
relationUpdates.push(updatePromise);
|
|
|
|
return acc;
|
|
|
|
}
|
|
|
|
case 'manyToOne': {
|
|
|
|
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 toAdd = _.difference(storedValue, currentValue);
|
|
|
|
const toRemove = _.difference(currentValue, storedValue);
|
|
|
|
|
|
|
|
const collection = this.forge({
|
|
|
|
[this.primaryKey]: primaryKeyValue,
|
|
|
|
})[association.alias]();
|
2019-07-24 17:16:50 +02:00
|
|
|
|
2019-07-05 17:17:39 +02:00
|
|
|
const updatePromise = collection
|
2019-07-24 17:16:50 +02:00
|
|
|
.detach(toRemove, { transacting })
|
|
|
|
.then(() => collection.attach(toAdd, { transacting }));
|
2019-07-05 17:17:39 +02:00
|
|
|
|
|
|
|
relationUpdates.push(updatePromise);
|
|
|
|
return acc;
|
|
|
|
}
|
|
|
|
case 'manyMorphToMany':
|
|
|
|
case 'manyMorphToOne':
|
|
|
|
// Update the relational array.
|
|
|
|
params.values[current].forEach(obj => {
|
2019-08-01 08:52:35 +02:00
|
|
|
const model = strapi.getModel(
|
|
|
|
obj.ref,
|
2019-07-05 17:17:39 +02:00
|
|
|
obj.source && obj.source !== 'content-manager'
|
2019-08-01 08:52:35 +02:00
|
|
|
? obj.source
|
|
|
|
: null
|
|
|
|
);
|
2019-07-05 17:17:39 +02:00
|
|
|
|
|
|
|
// Remove existing relationship because only one file
|
|
|
|
// can be related to this field.
|
|
|
|
if (association.nature === 'manyMorphToOne') {
|
|
|
|
relationUpdates.push(
|
|
|
|
module.exports.removeRelationMorph
|
2019-07-24 17:16:50 +02:00
|
|
|
.call(
|
|
|
|
this,
|
|
|
|
{
|
2019-07-05 17:17:39 +02:00
|
|
|
alias: association.alias,
|
|
|
|
ref: model.collectionName,
|
|
|
|
refId: obj.refId,
|
|
|
|
field: obj.field,
|
2019-07-24 17:16:50 +02:00
|
|
|
},
|
|
|
|
{ transacting }
|
|
|
|
)
|
|
|
|
.then(() =>
|
|
|
|
module.exports.addRelationMorph.call(
|
|
|
|
this,
|
|
|
|
{
|
|
|
|
id: response[this.primaryKey],
|
|
|
|
alias: association.alias,
|
|
|
|
ref: model.collectionName,
|
|
|
|
refId: obj.refId,
|
|
|
|
field: obj.field,
|
|
|
|
},
|
|
|
|
{ transacting }
|
|
|
|
)
|
2019-07-05 17:17:39 +02:00
|
|
|
)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
relationUpdates.push(
|
2019-07-24 17:16:50 +02:00
|
|
|
module.exports.addRelationMorph.call(
|
|
|
|
this,
|
|
|
|
{
|
|
|
|
id: response[this.primaryKey],
|
|
|
|
alias: association.alias,
|
|
|
|
ref: model.collectionName,
|
|
|
|
refId: obj.refId,
|
|
|
|
field: obj.field,
|
|
|
|
},
|
|
|
|
{ transacting }
|
|
|
|
)
|
2019-07-05 17:17:39 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
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 toAdd = _.difference(storedValue, currentValue);
|
|
|
|
const toRemove = _.difference(currentValue, storedValue);
|
|
|
|
|
|
|
|
const model = getModel(
|
|
|
|
details.collection || details.model,
|
|
|
|
details.plugin
|
|
|
|
);
|
|
|
|
|
|
|
|
toAdd.forEach(id => {
|
|
|
|
relationUpdates.push(
|
2019-07-24 17:16:50 +02:00
|
|
|
module.exports.addRelationMorph.call(
|
|
|
|
model,
|
|
|
|
{
|
|
|
|
id,
|
|
|
|
alias: association.via,
|
|
|
|
ref: this.collectionName,
|
|
|
|
refId: response.id,
|
|
|
|
field: association.alias,
|
|
|
|
},
|
|
|
|
{ transacting }
|
|
|
|
)
|
2019-07-05 17:17:39 +02:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Update the relational array.
|
|
|
|
toRemove.forEach(id => {
|
|
|
|
relationUpdates.push(
|
2019-07-24 17:16:50 +02:00
|
|
|
module.exports.removeRelationMorph.call(
|
|
|
|
model,
|
|
|
|
{
|
|
|
|
id,
|
|
|
|
alias: association.via,
|
|
|
|
ref: this.collectionName,
|
|
|
|
refId: response.id,
|
|
|
|
field: association.alias,
|
|
|
|
},
|
|
|
|
{ transacting }
|
|
|
|
)
|
2019-07-05 17:17:39 +02:00
|
|
|
);
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'oneMorphToOne':
|
2019-08-01 08:52:35 +02:00
|
|
|
case 'oneMorphToMany': {
|
2019-07-05 17:17:39 +02:00
|
|
|
break;
|
2019-08-01 08:52:35 +02:00
|
|
|
}
|
2019-07-05 17:17:39 +02:00
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
},
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
2019-07-11 18:08:50 +02:00
|
|
|
await Promise.all(relationUpdates);
|
|
|
|
|
2019-07-05 17:17:39 +02:00
|
|
|
delete values[this.primaryKey];
|
2018-05-09 16:08:58 +02:00
|
|
|
if (!_.isEmpty(values)) {
|
2019-07-12 11:25:26 +02:00
|
|
|
await this.forge({
|
|
|
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
|
|
|
}).save(values, {
|
|
|
|
patch: true,
|
2019-07-24 17:16:50 +02:00
|
|
|
transacting,
|
2018-05-09 16:08:58 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-08-01 08:52:35 +02:00
|
|
|
const result = await this.forge({
|
2019-07-05 17:17:39 +02:00
|
|
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
|
|
|
}).fetch({
|
2019-07-24 17:16:50 +02:00
|
|
|
transacting,
|
2019-07-05 17:17:39 +02:00
|
|
|
});
|
2019-08-01 08:52:35 +02:00
|
|
|
|
2019-08-01 15:59:21 +02:00
|
|
|
return result && result.toJSON ? result.toJSON() : result;
|
2018-05-09 16:08:58 +02:00
|
|
|
},
|
|
|
|
|
2019-07-24 17:16:50 +02:00
|
|
|
async addRelationMorph(params, { transacting } = {}) {
|
2019-07-05 17:17:39 +02:00
|
|
|
const record = await this.morph
|
|
|
|
.forge()
|
2018-05-09 16:08:58 +02:00
|
|
|
.where({
|
|
|
|
[`${this.collectionName}_id`]: params.id,
|
|
|
|
[`${params.alias}_id`]: params.refId,
|
|
|
|
[`${params.alias}_type`]: params.ref,
|
2019-07-05 17:17:39 +02:00
|
|
|
field: params.field,
|
2018-05-09 16:08:58 +02:00
|
|
|
})
|
|
|
|
.fetch({
|
2019-07-24 17:16:50 +02:00
|
|
|
transacting,
|
2018-05-09 16:08:58 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
const entry = record ? record.toJSON() : record;
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
2019-07-05 17:17:39 +02:00
|
|
|
return await this.morph
|
|
|
|
.forge({
|
2018-05-09 16:08:58 +02:00
|
|
|
[`${this.collectionName}_id`]: params.id,
|
|
|
|
[`${params.alias}_id`]: params.refId,
|
|
|
|
[`${params.alias}_type`]: params.ref,
|
2019-07-05 17:17:39 +02:00
|
|
|
field: params.field,
|
|
|
|
})
|
2019-07-24 17:16:50 +02:00
|
|
|
.save(null, { transacting });
|
2019-07-05 17:17:39 +02:00
|
|
|
},
|
|
|
|
|
2019-07-24 17:16:50 +02:00
|
|
|
async removeRelationMorph(params, { transacting } = {}) {
|
2019-07-05 17:17:39 +02:00
|
|
|
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
|
|
|
|
)
|
|
|
|
)
|
2019-05-22 18:39:25 +02:00
|
|
|
.destroy({
|
2019-07-05 17:17:39 +02:00
|
|
|
require: false,
|
2019-07-24 17:16:50 +02:00
|
|
|
transacting,
|
2019-05-22 18:39:25 +02:00
|
|
|
});
|
2019-07-05 17:17:39 +02:00
|
|
|
},
|
2018-05-16 12:07:02 +02:00
|
|
|
};
|