mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 19:04:38 +00:00
Init manWay relation
This commit is contained in:
parent
30e8a673f4
commit
d7e9509a45
@ -17,10 +17,6 @@
|
||||
"content2": {
|
||||
"type": "text"
|
||||
},
|
||||
"posts": {
|
||||
"collection": "post",
|
||||
"via": "articles"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -36,6 +32,12 @@
|
||||
"repeatable": true,
|
||||
"min": 2,
|
||||
"max": 3
|
||||
},
|
||||
"mainTag": {
|
||||
"model": "tag"
|
||||
},
|
||||
"linkedTags": {
|
||||
"collection": "tag"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,19 @@
|
||||
{
|
||||
"connection": "default",
|
||||
"collectionName": "",
|
||||
"collectionName": "post",
|
||||
"info": {
|
||||
"name": "post",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"timestamps": true
|
||||
"timestamps": [
|
||||
"created_at",
|
||||
"updated_at"
|
||||
]
|
||||
},
|
||||
"attributes": {
|
||||
"articles": {
|
||||
"collection": "articles",
|
||||
"dominant": true,
|
||||
"via": "posts"
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
17
examples/getstarted/api/tag/models/Tag.settings.json
Normal file
17
examples/getstarted/api/tag/models/Tag.settings.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"connection": "default",
|
||||
"collectionName": "tags",
|
||||
"info": {
|
||||
"name": "tag",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"increments": true,
|
||||
"timestamps": false
|
||||
},
|
||||
"attributes": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const { models: utilsModels } = require('strapi-utils');
|
||||
const { singular } = require('pluralize');
|
||||
|
||||
/* global StrapiConfigs */
|
||||
module.exports = async ({
|
||||
@ -309,36 +309,37 @@ module.exports = async ({
|
||||
}
|
||||
|
||||
// Equilize many to many releations
|
||||
const manyRelations = definition.associations.filter(association => {
|
||||
return association.nature === 'manyToMany';
|
||||
});
|
||||
const manyRelations = definition.associations.filter(({ nature }) =>
|
||||
['manyToMany', 'manyWay'].includes(nature)
|
||||
);
|
||||
|
||||
for (const manyRelation of manyRelations) {
|
||||
if (manyRelation && manyRelation.dominant) {
|
||||
const collection = manyRelation.plugin
|
||||
? strapi.plugins[manyRelation.plugin].models[manyRelation.collection]
|
||||
: strapi.models[manyRelation.collection];
|
||||
const { plugin, collection, via, dominant, alias } = manyRelation;
|
||||
|
||||
if (dominant) {
|
||||
const targetCollection = plugin
|
||||
? strapi.plugins[plugin].models[collection]
|
||||
: strapi.models[collection];
|
||||
|
||||
const targetAttr = via
|
||||
? targetCollection.attributes[via]
|
||||
: {
|
||||
attribute: singular(definition.collectionName),
|
||||
column: definition.primaryKey,
|
||||
};
|
||||
|
||||
const defAttr = definition.attributes[alias];
|
||||
|
||||
const attributes = {
|
||||
[`${collection.attributes[manyRelation.via].attribute}_${
|
||||
collection.attributes[manyRelation.via].column
|
||||
}`]: {
|
||||
type: collection.primaryKeyType,
|
||||
[`${targetAttr.attribute}_${targetAttr.column}`]: {
|
||||
type: targetCollection.primaryKeyType,
|
||||
},
|
||||
[`${definition.attributes[manyRelation.alias].attribute}_${
|
||||
definition.attributes[manyRelation.alias].column
|
||||
}`]: {
|
||||
[`${defAttr.attribute}_${defAttr.column}`]: {
|
||||
type: definition.primaryKeyType,
|
||||
},
|
||||
};
|
||||
|
||||
const table =
|
||||
_.get(manyRelation, 'collectionName') ||
|
||||
utilsModels.getCollectionName(
|
||||
collection.attributes[manyRelation.via],
|
||||
manyRelation
|
||||
);
|
||||
|
||||
const table = manyRelation.tableCollectionName;
|
||||
await createOrUpdateTable(table, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const pluralize = require('pluralize');
|
||||
const { singular } = require('pluralize');
|
||||
|
||||
const utilsModels = require('strapi-utils').models;
|
||||
const utils = require('./utils/');
|
||||
const relations = require('./relations');
|
||||
const buildDatabaseSchema = require('./buildDatabaseSchema');
|
||||
const genGroupRelatons = require('./generate-group-relations');
|
||||
@ -125,11 +124,9 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const verbose =
|
||||
_.get(
|
||||
utilsModels.getNature(details, name, undefined, model.toLowerCase()),
|
||||
'verbose'
|
||||
) || '';
|
||||
const { nature, verbose } =
|
||||
utilsModels.getNature(details, name, undefined, model.toLowerCase()) ||
|
||||
{};
|
||||
|
||||
// Build associations key
|
||||
utilsModels.defineAssociations(
|
||||
@ -211,58 +208,71 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||
break;
|
||||
}
|
||||
case 'belongsToMany': {
|
||||
const collection = details.plugin
|
||||
const targetModel = details.plugin
|
||||
? strapi.plugins[details.plugin].models[details.collection]
|
||||
: strapi.models[details.collection];
|
||||
|
||||
const collectionName =
|
||||
_.get(details, 'collectionName') ||
|
||||
utilsModels.getCollectionName(
|
||||
collection.attributes[details.via],
|
||||
details
|
||||
);
|
||||
|
||||
const relationship = collection.attributes[details.via];
|
||||
|
||||
// Force singular foreign key
|
||||
relationship.attribute = pluralize.singular(relationship.collection);
|
||||
details.attribute = pluralize.singular(details.collection);
|
||||
details.attribute = singular(details.collection);
|
||||
details.column = targetModel.primaryKey;
|
||||
|
||||
// Define PK column
|
||||
details.column = utils.getPK(model, strapi.models);
|
||||
relationship.column = utils.getPK(details.collection, strapi.models);
|
||||
let options = [];
|
||||
if (nature === 'manyWay') {
|
||||
const joinTableName = `${definition.collectionName}__${_.snakeCase(
|
||||
name
|
||||
)}`;
|
||||
|
||||
// Sometimes the many-to-many relationships
|
||||
// is on the same keys on the same models (ex: `friends` key in model `User`)
|
||||
if (
|
||||
`${details.attribute}_${details.column}` ===
|
||||
`${relationship.attribute}_${relationship.column}`
|
||||
) {
|
||||
relationship.attribute = pluralize.singular(details.via);
|
||||
const foreignKey = `${singular(definition.collectionName)}_${
|
||||
definition.primaryKey
|
||||
}`;
|
||||
|
||||
const otherKey = `${details.attribute}_${details.column}`;
|
||||
|
||||
options = [joinTableName, foreignKey, otherKey];
|
||||
} else {
|
||||
const joinTableName =
|
||||
_.get(details, 'collectionName') ||
|
||||
utilsModels.getCollectionName(
|
||||
targetModel.attributes[details.via],
|
||||
details
|
||||
);
|
||||
|
||||
const relationship = targetModel.attributes[details.via];
|
||||
|
||||
// Define PK column
|
||||
relationship.attribute = singular(relationship.collection);
|
||||
relationship.column = definition.primaryKey;
|
||||
|
||||
// Sometimes the many-to-many relationships
|
||||
// is on the same keys on the same models (ex: `friends` key in model `User`)
|
||||
if (
|
||||
`${details.attribute}_${details.column}` ===
|
||||
`${relationship.attribute}_${relationship.column}`
|
||||
) {
|
||||
relationship.attribute = singular(details.via);
|
||||
}
|
||||
|
||||
const foreignKey = `${relationship.attribute}_${relationship.column}`;
|
||||
const otherKey = `${details.attribute}_${details.column}`;
|
||||
|
||||
options = [joinTableName, foreignKey, otherKey];
|
||||
}
|
||||
|
||||
// Set this info to be able to see if this field is a real database's field.
|
||||
details.isVirtual = true;
|
||||
|
||||
loadedModel[name] = function() {
|
||||
if (
|
||||
_.isArray(_.get(details, 'withPivot')) &&
|
||||
!_.isEmpty(details.withPivot)
|
||||
) {
|
||||
return this.belongsToMany(
|
||||
GLOBALS[globalId],
|
||||
collectionName,
|
||||
`${relationship.attribute}_${relationship.column}`,
|
||||
`${details.attribute}_${details.column}`
|
||||
).withPivot(details.withPivot);
|
||||
const targetBookshelfModel = GLOBALS[globalId];
|
||||
let collection = this.belongsToMany(
|
||||
targetBookshelfModel,
|
||||
...options
|
||||
);
|
||||
|
||||
if (Array.isArray(details.withPivot)) {
|
||||
return collection.withPivot(details.withPivot);
|
||||
}
|
||||
|
||||
return this.belongsToMany(
|
||||
GLOBALS[globalId],
|
||||
collectionName,
|
||||
`${relationship.attribute}_${relationship.column}`,
|
||||
`${details.attribute}_${details.column}`
|
||||
);
|
||||
return collection;
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
@ -8,10 +8,12 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
// Utils
|
||||
const { models: { getValuePrimaryKey } } = require('strapi-utils');
|
||||
const {
|
||||
models: { getValuePrimaryKey },
|
||||
} = require('strapi-utils');
|
||||
|
||||
const transformToArrayID = (array, association) => {
|
||||
if(_.isArray(array)) {
|
||||
if (_.isArray(array)) {
|
||||
array = array.map(value => {
|
||||
if (_.isPlainObject(value)) {
|
||||
return value._id || value.id || false;
|
||||
@ -23,7 +25,10 @@ const transformToArrayID = (array, association) => {
|
||||
return array.filter(n => n);
|
||||
}
|
||||
|
||||
if (association.type === 'model' || (association.type === 'collection' && _.isObject(array))) {
|
||||
if (
|
||||
association.type === 'model' ||
|
||||
(association.type === 'collection' && _.isObject(array))
|
||||
) {
|
||||
return _.isEmpty(_.toString(array)) ? [] : transformToArrayID([array]);
|
||||
}
|
||||
|
||||
@ -31,31 +36,39 @@ const transformToArrayID = (array, association) => {
|
||||
};
|
||||
|
||||
const getModel = (model, plugin) => {
|
||||
return _.get(strapi.plugins, [plugin, 'models', model]) || _.get(strapi, ['models', model]) || undefined;
|
||||
return (
|
||||
_.get(strapi.plugins, [plugin, 'models', model]) ||
|
||||
_.get(strapi, ['models', model]) ||
|
||||
undefined
|
||||
);
|
||||
};
|
||||
|
||||
const removeUndefinedKeys = obj => _.pickBy(obj, _.negate(_.isUndefined));
|
||||
|
||||
module.exports = {
|
||||
findOne: async function (params, populate) {
|
||||
const record = await this
|
||||
.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
||||
})
|
||||
.fetch({
|
||||
withRelated: populate || this.associations.map(x => x.alias)
|
||||
});
|
||||
findOne: async function(params, populate) {
|
||||
const record = await this.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||
}).fetch({
|
||||
withRelated: populate || this.associations.map(x => x.alias),
|
||||
});
|
||||
|
||||
const data = record ? record.toJSON() : record;
|
||||
|
||||
// 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()
|
||||
return this.morph
|
||||
.forge()
|
||||
.where({
|
||||
[`${this.collectionName}_id`]: getValuePrimaryKey(params, this.primaryKey)
|
||||
[`${this.collectionName}_id`]: getValuePrimaryKey(
|
||||
params,
|
||||
this.primaryKey
|
||||
),
|
||||
})
|
||||
.fetchAll();
|
||||
});
|
||||
@ -70,185 +83,260 @@ module.exports = {
|
||||
return data;
|
||||
},
|
||||
|
||||
update: async function (params) {
|
||||
update: async function(params) {
|
||||
const relationUpdates = [];
|
||||
const primaryKeyValue = getValuePrimaryKey(params, this.primaryKey);
|
||||
const response = await module.exports.findOne.call(this, params);
|
||||
|
||||
// Only update fields which are on this document.
|
||||
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];
|
||||
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);
|
||||
}
|
||||
if (!association && _.get(details, 'isVirtual') !== true) {
|
||||
return _.set(acc, current, property);
|
||||
}
|
||||
|
||||
const assocModel = getModel(details.model || details.collection, details.plugin);
|
||||
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 }, {method: 'update', patch: true, require: false});
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return _.set(acc, current, null);
|
||||
}
|
||||
|
||||
|
||||
// set old relations to null
|
||||
const updateLink = this.where({ [current]: property })
|
||||
.save({ [current]: null }, {method: 'update', patch: true, require: false})
|
||||
.then(() => {
|
||||
return assocModel
|
||||
.where({ [this.primaryKey]: property })
|
||||
.save({ [details.via] : primaryKeyValue}, {method: 'update', patch: true, require: false});
|
||||
});
|
||||
|
||||
// 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 }, { method: 'update', patch: true, require: false })
|
||||
.then(() => {
|
||||
return assocModel
|
||||
.where(assocModel.primaryKey, 'in', property.map(val => val[assocModel.primaryKey]||val))
|
||||
.save({ [details.via] : primaryKeyValue }, { method: 'update', patch: true, require: false });
|
||||
});
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return acc;
|
||||
}
|
||||
case 'manyToOne': {
|
||||
return _.set(acc, current, _.get(property, assocModel.primaryKey, property));
|
||||
}
|
||||
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]();
|
||||
const updatePromise = collection
|
||||
.detach(toRemove)
|
||||
.then(() => collection.attach(toAdd));
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return acc;
|
||||
}
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne':
|
||||
// Update the relational array.
|
||||
params.values[current].forEach(obj => {
|
||||
const model = obj.source && obj.source !== 'content-manager' ?
|
||||
strapi.plugins[obj.source].models[obj.ref]:
|
||||
strapi.models[obj.ref];
|
||||
|
||||
// Remove existing relationship because only one file
|
||||
// can be related to this field.
|
||||
if (association.nature === 'manyMorphToOne') {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph.call(this, {
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field
|
||||
})
|
||||
.then(() =>
|
||||
module.exports.addRelationMorph.call(this, {
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field
|
||||
})
|
||||
)
|
||||
const assocModel = getModel(
|
||||
details.model || details.collection,
|
||||
details.plugin
|
||||
);
|
||||
} else {
|
||||
relationUpdates.push(module.exports.addRelationMorph.call(this, {
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field
|
||||
}));
|
||||
}
|
||||
});
|
||||
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);
|
||||
switch (association.nature) {
|
||||
case 'oneWay': {
|
||||
return _.set(
|
||||
acc,
|
||||
current,
|
||||
_.get(property, assocModel.primaryKey, property)
|
||||
);
|
||||
}
|
||||
case 'oneToOne': {
|
||||
if (response[current] === property) return acc;
|
||||
|
||||
const model = getModel(details.collection || details.model, details.plugin);
|
||||
if (_.isNull(property)) {
|
||||
const updatePromise = assocModel
|
||||
.where({
|
||||
[assocModel.primaryKey]: getValuePrimaryKey(
|
||||
response[current],
|
||||
assocModel.primaryKey
|
||||
),
|
||||
})
|
||||
.save(
|
||||
{ [details.via]: null },
|
||||
{ method: 'update', patch: true, require: false }
|
||||
);
|
||||
|
||||
toAdd.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.addRelationMorph.call(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.collectionName,
|
||||
refId: response.id,
|
||||
field: association.alias
|
||||
})
|
||||
);
|
||||
});
|
||||
relationUpdates.push(updatePromise);
|
||||
return _.set(acc, current, null);
|
||||
}
|
||||
|
||||
// Update the relational array.
|
||||
toRemove.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph.call(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.collectionName,
|
||||
refId: response.id,
|
||||
field: association.alias
|
||||
})
|
||||
);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'oneMorphToOne':
|
||||
case 'oneMorphToMany':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
// set old relations to null
|
||||
const updateLink = this.where({ [current]: property })
|
||||
.save(
|
||||
{ [current]: null },
|
||||
{ method: 'update', patch: true, require: false }
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel
|
||||
.where({ [this.primaryKey]: property })
|
||||
.save(
|
||||
{ [details.via]: primaryKeyValue },
|
||||
{ method: 'update', patch: true, require: false }
|
||||
);
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
// 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 },
|
||||
{ method: 'update', patch: true, require: false }
|
||||
)
|
||||
.then(() => {
|
||||
return assocModel
|
||||
.where(
|
||||
assocModel.primaryKey,
|
||||
'in',
|
||||
property.map(val => val[assocModel.primaryKey] || val)
|
||||
)
|
||||
.save(
|
||||
{ [details.via]: primaryKeyValue },
|
||||
{ method: 'update', patch: true, require: false }
|
||||
);
|
||||
});
|
||||
|
||||
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]();
|
||||
const updatePromise = collection
|
||||
.detach(toRemove)
|
||||
.then(() => collection.attach(toAdd));
|
||||
|
||||
relationUpdates.push(updatePromise);
|
||||
return acc;
|
||||
}
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne':
|
||||
// Update the relational array.
|
||||
params.values[current].forEach(obj => {
|
||||
const model =
|
||||
obj.source && obj.source !== 'content-manager'
|
||||
? strapi.plugins[obj.source].models[obj.ref]
|
||||
: strapi.models[obj.ref];
|
||||
|
||||
// Remove existing relationship because only one file
|
||||
// can be related to this field.
|
||||
if (association.nature === 'manyMorphToOne') {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph
|
||||
.call(this, {
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
})
|
||||
.then(() =>
|
||||
module.exports.addRelationMorph.call(this, {
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
})
|
||||
)
|
||||
);
|
||||
} else {
|
||||
relationUpdates.push(
|
||||
module.exports.addRelationMorph.call(this, {
|
||||
id: response[this.primaryKey],
|
||||
alias: association.alias,
|
||||
ref: model.collectionName,
|
||||
refId: obj.refId,
|
||||
field: obj.field,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
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(
|
||||
module.exports.addRelationMorph.call(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.collectionName,
|
||||
refId: response.id,
|
||||
field: association.alias,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Update the relational array.
|
||||
toRemove.forEach(id => {
|
||||
relationUpdates.push(
|
||||
module.exports.removeRelationMorph.call(model, {
|
||||
id,
|
||||
alias: association.via,
|
||||
ref: this.collectionName,
|
||||
refId: response.id,
|
||||
field: association.alias,
|
||||
})
|
||||
);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'oneMorphToOne':
|
||||
case 'oneMorphToMany':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
delete values[this.primaryKey];
|
||||
if (!_.isEmpty(values)) {
|
||||
relationUpdates.push(
|
||||
this
|
||||
.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
||||
})
|
||||
.save(values, {
|
||||
patch: true
|
||||
})
|
||||
this.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||
}).save(values, {
|
||||
patch: true,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
relationUpdates.push(Promise.resolve(_.assign(response, params.values)));
|
||||
@ -257,17 +345,17 @@ module.exports = {
|
||||
// Update virtuals fields.
|
||||
await Promise.all(relationUpdates);
|
||||
|
||||
return await this
|
||||
.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
||||
})
|
||||
.fetch({
|
||||
withRelated: this.associations.map(x => x.alias)
|
||||
});
|
||||
return await this.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||
}).fetch({
|
||||
withRelated: this.associations.map(x => x.alias),
|
||||
});
|
||||
},
|
||||
|
||||
addRelation: async function (params) {
|
||||
const association = this.associations.find(x => x.via === params.foreignKey && _.get(params.values, x.alias, null));
|
||||
addRelation: async function(params) {
|
||||
const association = this.associations.find(
|
||||
x => x.via === params.foreignKey && _.get(params.values, x.alias, null)
|
||||
);
|
||||
|
||||
if (!association) {
|
||||
// Resolve silently.
|
||||
@ -281,16 +369,20 @@ module.exports = {
|
||||
return module.exports.update.call(this, params);
|
||||
case 'manyToMany':
|
||||
return this.forge({
|
||||
[this.primaryKey]: params[this.primaryKey]
|
||||
})[association.alias]().attach(params.values[association.alias]);
|
||||
[this.primaryKey]: params[this.primaryKey],
|
||||
})
|
||||
[association.alias]()
|
||||
.attach(params.values[association.alias]);
|
||||
default:
|
||||
// Resolve silently.
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
removeRelation: async function (params) {
|
||||
const association = this.associations.find(x => x.via === params.foreignKey && _.get(params.values, x.alias, null));
|
||||
removeRelation: async function(params) {
|
||||
const association = this.associations.find(
|
||||
x => x.via === params.foreignKey && _.get(params.values, x.alias, null)
|
||||
);
|
||||
|
||||
if (!association) {
|
||||
// Resolve silently.
|
||||
@ -304,24 +396,27 @@ module.exports = {
|
||||
return module.exports.update.call(this, params);
|
||||
case 'manyToMany':
|
||||
return this.forge({
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
||||
})[association.alias]().detach(params.values[association.alias]);
|
||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||
})
|
||||
[association.alias]()
|
||||
.detach(params.values[association.alias]);
|
||||
default:
|
||||
// Resolve silently.
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
addRelationMorph: async function (params) {
|
||||
const record = await this.morph.forge()
|
||||
addRelationMorph: async function(params) {
|
||||
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
|
||||
field: params.field,
|
||||
})
|
||||
.fetch({
|
||||
withRelated: this.associations.map(x => x.alias)
|
||||
withRelated: this.associations.map(x => x.alias),
|
||||
});
|
||||
|
||||
const entry = record ? record.toJSON() : record;
|
||||
@ -330,25 +425,32 @@ module.exports = {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return await this.morph.forge({
|
||||
[`${this.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field
|
||||
})
|
||||
.save();
|
||||
},
|
||||
|
||||
removeRelationMorph: async function (params) {
|
||||
return await this.morph.forge()
|
||||
.where(_.omitBy({
|
||||
return await this.morph
|
||||
.forge({
|
||||
[`${this.collectionName}_id`]: params.id,
|
||||
[`${params.alias}_id`]: params.refId,
|
||||
[`${params.alias}_type`]: params.ref,
|
||||
field: params.field
|
||||
}, _.isUndefined))
|
||||
field: params.field,
|
||||
})
|
||||
.save();
|
||||
},
|
||||
|
||||
removeRelationMorph: async function(params) {
|
||||
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
|
||||
require: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,154 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
// Public node modules.
|
||||
const _ = require('lodash');
|
||||
|
||||
// Strapi helper for GraphQL.
|
||||
const helpers = require('strapi/lib/configuration/hooks/graphql/helpers/'); // eslint-disable-line import/no-unresolved
|
||||
const utils = require('./');
|
||||
|
||||
/**
|
||||
* Utils functions for BookShelf
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Get collection identity
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
getCollectionIdentity: collection => {
|
||||
return _.capitalize(collection.forge().tableName);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch one record
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
fetch: (collectionIdentity, collection, criteria) => {
|
||||
return collection.forge(criteria)
|
||||
.fetch({withRelated: helpers.getAssociationsByIdentity(collectionIdentity)})
|
||||
.then(data => _.isEmpty(data) ? data : data.toJSON());
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch all records
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
fetchAll: (collectionIdentity, collection, criteria) => {
|
||||
const filters = _.omit(helpers.handleFilters(criteria), value => {
|
||||
return _.isUndefined(value) || _.isNumber(value) ? _.isNull(value) : _.isEmpty(value);
|
||||
});
|
||||
|
||||
return collection.forge()
|
||||
.query(filters)
|
||||
.fetchAll({withRelated: helpers.getAssociationsByIdentity(collectionIdentity)})
|
||||
.then(data => data.toJSON() || data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch latests records based on criteria
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
fetchLatest: (collectionIdentity, collection, criteria) => {
|
||||
const filters = _.omit(helpers.handleFilters(criteria), value => {
|
||||
return _.isUndefined(value) || _.isNumber(value) ? _.isNull(value) : _.isEmpty(value);
|
||||
});
|
||||
|
||||
// Handle filters
|
||||
filters.orderBy = 'createdAt DESC';
|
||||
filters.limit = filters.count;
|
||||
|
||||
delete filters.count;
|
||||
|
||||
return collection.forge(criteria)
|
||||
.query(filters)
|
||||
.fetchAll({withRelated: helpers.getAssociationsByIdentity(collectionIdentity)})
|
||||
.then(data => data.toJSON() || data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch first records based on criteria
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
fetchFirst: (collectionIdentity, collection, criteria) => {
|
||||
const filters = _.omit(helpers.handleFilters(criteria), value => {
|
||||
return _.isUndefined(value) || _.isNumber(value) ? _.isNull(value) : _.isEmpty(value);
|
||||
});
|
||||
|
||||
// Handle filters
|
||||
filters.orderBy = 'createdAt ASC';
|
||||
filters.limit = filters.count;
|
||||
|
||||
delete filters.count;
|
||||
|
||||
return collection.forge(criteria)
|
||||
.query(filters)
|
||||
.fetchAll({withRelated: helpers.getAssociationsByIdentity(collectionIdentity)})
|
||||
.then(data => data.toJSON() || data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Create record
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
create: (collectionIdentity, rootValue) => {
|
||||
return strapi.services[collectionIdentity.toLowerCase()]
|
||||
.add(rootValue.context.request.body)
|
||||
.then(data => _.isFunction(_.get(data, 'toJSON')) ? data.toJSON() : data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update record
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
update: (collectionIdentity, rootValue, args) => {
|
||||
_.merge(args, rootValue.context.request.body);
|
||||
|
||||
const PK = utils.getPK(collectionIdentity.toLowerCase(), null, strapi.models);
|
||||
|
||||
return strapi.services[collectionIdentity.toLowerCase()]
|
||||
.edit(_.set({}, PK, args[PK]), _.omit(args, PK))
|
||||
.then(data => _.isFunction(_.get(data, 'toJSON')) ? data.toJSON() : data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete record
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
|
||||
delete: (collectionIdentity, rootValue, args) => {
|
||||
_.merge(args, rootValue.context.request.body);
|
||||
|
||||
return strapi.services[collectionIdentity.toLowerCase()]
|
||||
.remove(args)
|
||||
.then(data => _.isFunction(_.get(data, 'toJSON')) ? data.toJSON() : data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Count records
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
count: (collectionIdentity, collection) => collection.forge().count()
|
||||
};
|
||||
@ -1,58 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
// Public node modules.
|
||||
const _ = require('lodash');
|
||||
|
||||
/**
|
||||
* Utils functions for BookShelf
|
||||
*/
|
||||
/* eslint-disable prefer-template */
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Find primary key
|
||||
*/
|
||||
|
||||
getPK: (collectionIdentity, collection, models) => {
|
||||
// This is not a Bookshelf collection, only the name.
|
||||
if (_.isString(collectionIdentity) && !_.isUndefined(models)) {
|
||||
const PK = _.findKey(_.get(models, collectionIdentity + '.attributes'), o => {
|
||||
return o.hasOwnProperty('primary');
|
||||
});
|
||||
|
||||
if (!_.isEmpty(PK)) {
|
||||
return PK;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (_.isObject(collection)) {
|
||||
return collection.forge().idAttribute || 'id';
|
||||
}
|
||||
} catch (e) {
|
||||
// Collection undefined try to get the collection based on collectionIdentity
|
||||
if (typeof strapi !== 'undefined') {
|
||||
collection = _.get(strapi, `bookshelf.collections.${collectionIdentity}`);
|
||||
}
|
||||
|
||||
// Impossible to match collectionIdentity before, try to use idAttribute
|
||||
if (_.isObject(collection)) {
|
||||
return collection.forge().idAttribute || 'id';
|
||||
}
|
||||
}
|
||||
|
||||
return 'id';
|
||||
},
|
||||
|
||||
/**
|
||||
* Find primary key
|
||||
*/
|
||||
|
||||
getCount: type => {
|
||||
return strapi.bookshelf.collections[type].forge().count().then(count => count);
|
||||
}
|
||||
};
|
||||
@ -4,9 +4,6 @@
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
// Node.js core
|
||||
const path = require('path');
|
||||
|
||||
// Public node modules.
|
||||
const _ = require('lodash');
|
||||
const pluralize = require('pluralize');
|
||||
@ -29,33 +26,6 @@ module.exports = {
|
||||
cb();
|
||||
},
|
||||
|
||||
/**
|
||||
* Find primary key per ORM
|
||||
*/
|
||||
|
||||
getPK: function(collectionIdentity, collection, models) {
|
||||
if (_.isString(collectionIdentity)) {
|
||||
const ORM = this.getORM(collectionIdentity);
|
||||
try {
|
||||
const GraphQLFunctions = require(path.resolve(
|
||||
strapi.config.appPath,
|
||||
'node_modules',
|
||||
'strapi-' + ORM,
|
||||
'lib',
|
||||
'utils',
|
||||
));
|
||||
|
||||
if (!_.isUndefined(GraphQLFunctions)) {
|
||||
return GraphQLFunctions.getPK(collectionIdentity, collection, models || strapi.models);
|
||||
}
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the value based on the primary key
|
||||
*/
|
||||
@ -64,34 +34,6 @@ module.exports = {
|
||||
return value[defaultKey] || value.id || value._id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find primary key per ORM
|
||||
*/
|
||||
|
||||
getCount: function(collectionIdentity) {
|
||||
if (_.isString(collectionIdentity)) {
|
||||
const ORM = this.getORM(collectionIdentity);
|
||||
|
||||
try {
|
||||
const ORMFunctions = require(path.resolve(
|
||||
strapi.config.appPath,
|
||||
'node_modules',
|
||||
'strapi-' + ORM,
|
||||
'lib',
|
||||
'utils',
|
||||
));
|
||||
|
||||
if (!_.isUndefined(ORMFunctions)) {
|
||||
return ORMFunctions.getCount(collectionIdentity);
|
||||
}
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find relation nature with verbose
|
||||
*/
|
||||
@ -104,11 +46,14 @@ module.exports = {
|
||||
};
|
||||
|
||||
if (_.isUndefined(models)) {
|
||||
models = association.plugin ? strapi.plugins[association.plugin].models : strapi.models;
|
||||
models = association.plugin
|
||||
? strapi.plugins[association.plugin].models
|
||||
: strapi.models;
|
||||
}
|
||||
|
||||
if (
|
||||
(association.hasOwnProperty('collection') && association.collection === '*') ||
|
||||
(association.hasOwnProperty('collection') &&
|
||||
association.collection === '*') ||
|
||||
(association.hasOwnProperty('model') && association.model === '*')
|
||||
) {
|
||||
if (association.model) {
|
||||
@ -117,20 +62,28 @@ module.exports = {
|
||||
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];
|
||||
});
|
||||
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;
|
||||
}, {});
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const allModels = _.merge({}, strapi.models, flattenedPluginsModels);
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
_.forIn(allModels, model => {
|
||||
_.forIn(model.attributes, attribute => {
|
||||
if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.model === currentModelName) {
|
||||
if (
|
||||
attribute.hasOwnProperty('via') &&
|
||||
attribute.via === key &&
|
||||
attribute.model === currentModelName
|
||||
) {
|
||||
if (attribute.hasOwnProperty('collection')) {
|
||||
types.other = 'collection';
|
||||
|
||||
@ -145,14 +98,22 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
|
||||
const relatedAttribute = models[association.collection].attributes[association.via];
|
||||
} else if (
|
||||
association.hasOwnProperty('via') &&
|
||||
association.hasOwnProperty('collection')
|
||||
) {
|
||||
const relatedAttribute =
|
||||
models[association.collection].attributes[association.via];
|
||||
|
||||
if (!relatedAttribute) {
|
||||
throw new Error(
|
||||
`The attribute \`${association.via}\` is missing in the model ${_.upperFirst(association.collection)} ${
|
||||
`The attribute \`${
|
||||
association.via
|
||||
}\` is missing in the model ${_.upperFirst(
|
||||
association.collection
|
||||
)} ${
|
||||
association.plugin ? '(plugin - ' + association.plugin + ')' : ''
|
||||
}`,
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -170,12 +131,21 @@ module.exports = {
|
||||
!relatedAttribute.hasOwnProperty('via')
|
||||
) {
|
||||
types.other = 'collectionD';
|
||||
} else if (relatedAttribute.hasOwnProperty('model') && relatedAttribute.model !== '*') {
|
||||
} else if (
|
||||
relatedAttribute.hasOwnProperty('model') &&
|
||||
relatedAttribute.model !== '*'
|
||||
) {
|
||||
types.other = 'model';
|
||||
} else if (relatedAttribute.hasOwnProperty('collection') || relatedAttribute.hasOwnProperty('model')) {
|
||||
} else if (
|
||||
relatedAttribute.hasOwnProperty('collection') ||
|
||||
relatedAttribute.hasOwnProperty('model')
|
||||
) {
|
||||
types.other = 'morphTo';
|
||||
}
|
||||
} else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) {
|
||||
} else if (
|
||||
association.hasOwnProperty('via') &&
|
||||
association.hasOwnProperty('model')
|
||||
) {
|
||||
types.current = 'modelD';
|
||||
|
||||
// We have to find if they are a model linked to this key
|
||||
@ -189,9 +159,15 @@ module.exports = {
|
||||
attribute.collection !== '*'
|
||||
) {
|
||||
types.other = 'collection';
|
||||
} else if (attribute.hasOwnProperty('model') && attribute.model !== '*') {
|
||||
} else if (
|
||||
attribute.hasOwnProperty('model') &&
|
||||
attribute.model !== '*'
|
||||
) {
|
||||
types.other = 'model';
|
||||
} else if (attribute.hasOwnProperty('collection') || attribute.hasOwnProperty('model')) {
|
||||
} else if (
|
||||
attribute.hasOwnProperty('collection') ||
|
||||
attribute.hasOwnProperty('model')
|
||||
) {
|
||||
types.other = 'morphTo';
|
||||
}
|
||||
} else if (association.hasOwnProperty('model')) {
|
||||
@ -268,14 +244,18 @@ module.exports = {
|
||||
nature: 'oneMorphToOne',
|
||||
verbose: 'belongsToMorph',
|
||||
};
|
||||
} else if (types.current === 'morphTo' && (types.other === 'model' || association.hasOwnProperty('model'))) {
|
||||
} else if (
|
||||
types.current === 'morphTo' &&
|
||||
(types.other === 'model' || association.hasOwnProperty('model'))
|
||||
) {
|
||||
return {
|
||||
nature: 'manyMorphToOne',
|
||||
verbose: 'belongsToManyMorph',
|
||||
};
|
||||
} else if (
|
||||
types.current === 'morphTo' &&
|
||||
(types.other === 'collection' || association.hasOwnProperty('collection'))
|
||||
(types.other === 'collection' ||
|
||||
association.hasOwnProperty('collection'))
|
||||
) {
|
||||
return {
|
||||
nature: 'manyMorphToMany',
|
||||
@ -291,7 +271,10 @@ module.exports = {
|
||||
nature: 'oneToOne',
|
||||
verbose: 'hasOne',
|
||||
};
|
||||
} else if ((types.current === 'model' || types.current === 'modelD') && types.other === 'collection') {
|
||||
} else if (
|
||||
(types.current === 'model' || types.current === 'modelD') &&
|
||||
types.other === 'collection'
|
||||
) {
|
||||
return {
|
||||
nature: 'manyToOne',
|
||||
verbose: 'belongsTo',
|
||||
@ -306,7 +289,10 @@ 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',
|
||||
@ -334,21 +320,15 @@ module.exports = {
|
||||
return undefined;
|
||||
} catch (e) {
|
||||
strapi.log.error(
|
||||
`Something went wrong in the model \`${_.upperFirst(currentModelName)}\` with the attribute \`${key}\``,
|
||||
`Something went wrong in the model \`${_.upperFirst(
|
||||
currentModelName
|
||||
)}\` with the attribute \`${key}\``
|
||||
);
|
||||
strapi.log.error(e);
|
||||
strapi.stop();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return ORM used for this collection.
|
||||
*/
|
||||
|
||||
getORM: collectionIdentity => {
|
||||
return _.get(strapi.models, collectionIdentity.toLowerCase() + '.orm');
|
||||
},
|
||||
|
||||
/**
|
||||
* Return table name for a collection many-to-many
|
||||
*/
|
||||
@ -363,9 +343,7 @@ module.exports = {
|
||||
})
|
||||
.map(table =>
|
||||
_.snakeCase(
|
||||
`${pluralize.plural(table.collection)} ${pluralize.plural(
|
||||
table.via
|
||||
)}`
|
||||
`${pluralize.plural(table.collection)} ${pluralize.plural(table.via)}`
|
||||
)
|
||||
)
|
||||
.join('__');
|
||||
@ -383,23 +361,44 @@ module.exports = {
|
||||
}
|
||||
|
||||
// Exclude non-relational attribute
|
||||
if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) {
|
||||
return undefined;
|
||||
if (!_.has(association, 'collection') && !_.has(association, 'model')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get relation nature
|
||||
let details;
|
||||
const globalName = association.model || association.collection || '';
|
||||
const infos = this.getNature(association, key, undefined, model.toLowerCase());
|
||||
const targetName = association.model || association.collection || '';
|
||||
const infos = this.getNature(
|
||||
association,
|
||||
key,
|
||||
undefined,
|
||||
model.toLowerCase()
|
||||
);
|
||||
|
||||
if (globalName !== '*') {
|
||||
details = association.plugin
|
||||
? _.get(strapi.plugins, `${association.plugin}.models.${globalName}.attributes.${association.via}`, {})
|
||||
: _.get(strapi.models, `${globalName}.attributes.${association.via}`, {});
|
||||
if (targetName !== '*') {
|
||||
if (association.plugin) {
|
||||
details = _.get(
|
||||
strapi.plugins,
|
||||
[
|
||||
association.plugin,
|
||||
'models',
|
||||
targetName,
|
||||
'attributes',
|
||||
association.via,
|
||||
],
|
||||
{}
|
||||
);
|
||||
} else {
|
||||
details = _.get(
|
||||
strapi.models,
|
||||
[targetName, 'attributes', association.via],
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Build associations object
|
||||
if (association.hasOwnProperty('collection') && association.collection !== '*') {
|
||||
if (_.has(association, 'collection') && association.collection !== '*') {
|
||||
const ast = {
|
||||
alias: key,
|
||||
type: 'collection',
|
||||
@ -413,11 +412,22 @@ module.exports = {
|
||||
};
|
||||
|
||||
if (infos.nature === 'manyToMany' && definition.orm === 'bookshelf') {
|
||||
ast.tableCollectionName = this.getCollectionName(association, details);
|
||||
ast.tableCollectionName =
|
||||
_.get(association, 'collectionName') ||
|
||||
this.getCollectionName(details, association);
|
||||
}
|
||||
|
||||
if (infos.nature === 'manyWay' && definition.orm === 'bookshelf') {
|
||||
ast.tableCollectionName = `${
|
||||
definition.collectionName
|
||||
}__${_.snakeCase(key)}`;
|
||||
}
|
||||
|
||||
definition.associations.push(ast);
|
||||
} else if (association.hasOwnProperty('model') && association.model !== '*') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.has(association, 'model') && association.model !== '*') {
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: 'model',
|
||||
@ -429,15 +439,23 @@ module.exports = {
|
||||
plugin: association.plugin || undefined,
|
||||
filter: details.filter,
|
||||
});
|
||||
} else if (association.hasOwnProperty('collection') || association.hasOwnProperty('model')) {
|
||||
const pluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||
return;
|
||||
}
|
||||
|
||||
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() &&
|
||||
strapi.plugins[current].models[entity].globalId !== definition.globalId
|
||||
(attr.collection || attr.model || '').toLowerCase() ===
|
||||
model.toLowerCase() &&
|
||||
strapi.plugins[current].models[entity].globalId !==
|
||||
definition.globalId
|
||||
) {
|
||||
acc.push(strapi.plugins[current].models[entity].globalId);
|
||||
}
|
||||
@ -445,48 +463,52 @@ module.exports = {
|
||||
});
|
||||
|
||||
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];
|
||||
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() &&
|
||||
strapi.models[entity].globalId !== definition.globalId
|
||||
) {
|
||||
acc.push(strapi.models[entity].globalId);
|
||||
}
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const models = _.uniq(appModels.concat(pluginsModels));
|
||||
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: association.model ? 'model' : 'collection',
|
||||
related: models,
|
||||
nature: infos.nature,
|
||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||
filter: association.filter,
|
||||
if (
|
||||
(attr.collection || attr.model || '').toLowerCase() ===
|
||||
model.toLowerCase() &&
|
||||
strapi.models[entity].globalId !== definition.globalId
|
||||
) {
|
||||
acc.push(strapi.models[entity].globalId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const models = _.uniq(appModels.concat(pluginsModels));
|
||||
|
||||
definition.associations.push({
|
||||
alias: key,
|
||||
type: association.model ? 'model' : 'collection',
|
||||
related: models,
|
||||
nature: infos.nature,
|
||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||
filter: association.filter,
|
||||
});
|
||||
} catch (e) {
|
||||
strapi.log.error(`Something went wrong in the model \`${_.upperFirst(model)}\` with the attribute \`${key}\``);
|
||||
strapi.log.error(
|
||||
`Something went wrong in the model \`${_.upperFirst(
|
||||
model
|
||||
)}\` with the attribute \`${key}\``
|
||||
);
|
||||
strapi.log.error(e);
|
||||
strapi.stop();
|
||||
}
|
||||
},
|
||||
|
||||
getVia: (attribute, association) => {
|
||||
return _.findKey(strapi.models[association.model || association.collection].attributes, { via: attribute });
|
||||
},
|
||||
|
||||
convertParams: (entity, params) => {
|
||||
if (!entity) {
|
||||
throw new Error("You can't call the convert params method without passing the model's name as a first argument.");
|
||||
throw new Error(
|
||||
"You can't call the convert params method without passing the model's name as a first argument."
|
||||
);
|
||||
}
|
||||
|
||||
// Remove the source params (that can be sent from the ctm plugin) since it is not a filter
|
||||
@ -502,7 +524,7 @@ module.exports = {
|
||||
Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||
_.assign(acc, _.get(strapi.plugins[current], ['models'], {}));
|
||||
return acc;
|
||||
}, {}),
|
||||
}, {})
|
||||
);
|
||||
|
||||
if (!models.hasOwnProperty(model)) {
|
||||
@ -513,7 +535,9 @@ 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.hook[connector].load().getQueryParams;
|
||||
@ -555,13 +579,31 @@ module.exports = {
|
||||
} else {
|
||||
const suffix = key.split('_');
|
||||
// Mysql stores boolean as 1 or 0
|
||||
if (client === 'mysql' && _.get(models, [model, 'attributes', suffix, 'type']) === 'boolean') {
|
||||
if (
|
||||
client === 'mysql' &&
|
||||
_.get(models, [model, 'attributes', suffix, 'type']) === 'boolean'
|
||||
) {
|
||||
formattedValue = value.toString() === 'true' ? '1' : '0';
|
||||
}
|
||||
|
||||
let type;
|
||||
|
||||
if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss', 'in', 'nin'], _.last(suffix))) {
|
||||
if (
|
||||
_.includes(
|
||||
[
|
||||
'ne',
|
||||
'lt',
|
||||
'gt',
|
||||
'lte',
|
||||
'gte',
|
||||
'contains',
|
||||
'containss',
|
||||
'in',
|
||||
'nin',
|
||||
],
|
||||
_.last(suffix)
|
||||
)
|
||||
) {
|
||||
type = `_${_.last(suffix)}`;
|
||||
key = _.dropRight(suffix).join('_');
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user