mirror of
https://github.com/strapi/strapi.git
synced 2025-11-07 21:58:23 +00:00
Init manWay relation
This commit is contained in:
parent
30e8a673f4
commit
d7e9509a45
@ -17,10 +17,6 @@
|
|||||||
"content2": {
|
"content2": {
|
||||||
"type": "text"
|
"type": "text"
|
||||||
},
|
},
|
||||||
"posts": {
|
|
||||||
"collection": "post",
|
|
||||||
"via": "articles"
|
|
||||||
},
|
|
||||||
"title": {
|
"title": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -36,6 +32,12 @@
|
|||||||
"repeatable": true,
|
"repeatable": true,
|
||||||
"min": 2,
|
"min": 2,
|
||||||
"max": 3
|
"max": 3
|
||||||
|
},
|
||||||
|
"mainTag": {
|
||||||
|
"model": "tag"
|
||||||
|
},
|
||||||
|
"linkedTags": {
|
||||||
|
"collection": "tag"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,18 +1,19 @@
|
|||||||
{
|
{
|
||||||
"connection": "default",
|
"connection": "default",
|
||||||
"collectionName": "",
|
"collectionName": "post",
|
||||||
"info": {
|
"info": {
|
||||||
"name": "post",
|
"name": "post",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"timestamps": true
|
"timestamps": [
|
||||||
|
"created_at",
|
||||||
|
"updated_at"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"articles": {
|
"title": {
|
||||||
"collection": "articles",
|
"type": "string"
|
||||||
"dominant": true,
|
|
||||||
"via": "posts"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 _ = require('lodash');
|
||||||
const { models: utilsModels } = require('strapi-utils');
|
const { singular } = require('pluralize');
|
||||||
|
|
||||||
/* global StrapiConfigs */
|
/* global StrapiConfigs */
|
||||||
module.exports = async ({
|
module.exports = async ({
|
||||||
@ -309,36 +309,37 @@ module.exports = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Equilize many to many releations
|
// Equilize many to many releations
|
||||||
const manyRelations = definition.associations.filter(association => {
|
const manyRelations = definition.associations.filter(({ nature }) =>
|
||||||
return association.nature === 'manyToMany';
|
['manyToMany', 'manyWay'].includes(nature)
|
||||||
});
|
);
|
||||||
|
|
||||||
for (const manyRelation of manyRelations) {
|
for (const manyRelation of manyRelations) {
|
||||||
if (manyRelation && manyRelation.dominant) {
|
const { plugin, collection, via, dominant, alias } = manyRelation;
|
||||||
const collection = manyRelation.plugin
|
|
||||||
? strapi.plugins[manyRelation.plugin].models[manyRelation.collection]
|
if (dominant) {
|
||||||
: strapi.models[manyRelation.collection];
|
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 = {
|
const attributes = {
|
||||||
[`${collection.attributes[manyRelation.via].attribute}_${
|
[`${targetAttr.attribute}_${targetAttr.column}`]: {
|
||||||
collection.attributes[manyRelation.via].column
|
type: targetCollection.primaryKeyType,
|
||||||
}`]: {
|
|
||||||
type: collection.primaryKeyType,
|
|
||||||
},
|
},
|
||||||
[`${definition.attributes[manyRelation.alias].attribute}_${
|
[`${defAttr.attribute}_${defAttr.column}`]: {
|
||||||
definition.attributes[manyRelation.alias].column
|
|
||||||
}`]: {
|
|
||||||
type: definition.primaryKeyType,
|
type: definition.primaryKeyType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const table =
|
const table = manyRelation.tableCollectionName;
|
||||||
_.get(manyRelation, 'collectionName') ||
|
|
||||||
utilsModels.getCollectionName(
|
|
||||||
collection.attributes[manyRelation.via],
|
|
||||||
manyRelation
|
|
||||||
);
|
|
||||||
|
|
||||||
await createOrUpdateTable(table, attributes);
|
await createOrUpdateTable(table, attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const pluralize = require('pluralize');
|
const { singular } = require('pluralize');
|
||||||
|
|
||||||
const utilsModels = require('strapi-utils').models;
|
const utilsModels = require('strapi-utils').models;
|
||||||
const utils = require('./utils/');
|
|
||||||
const relations = require('./relations');
|
const relations = require('./relations');
|
||||||
const buildDatabaseSchema = require('./buildDatabaseSchema');
|
const buildDatabaseSchema = require('./buildDatabaseSchema');
|
||||||
const genGroupRelatons = require('./generate-group-relations');
|
const genGroupRelatons = require('./generate-group-relations');
|
||||||
@ -125,11 +124,9 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const verbose =
|
const { nature, verbose } =
|
||||||
_.get(
|
utilsModels.getNature(details, name, undefined, model.toLowerCase()) ||
|
||||||
utilsModels.getNature(details, name, undefined, model.toLowerCase()),
|
{};
|
||||||
'verbose'
|
|
||||||
) || '';
|
|
||||||
|
|
||||||
// Build associations key
|
// Build associations key
|
||||||
utilsModels.defineAssociations(
|
utilsModels.defineAssociations(
|
||||||
@ -211,26 +208,40 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'belongsToMany': {
|
case 'belongsToMany': {
|
||||||
const collection = details.plugin
|
const targetModel = details.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.collection]
|
? strapi.plugins[details.plugin].models[details.collection]
|
||||||
: strapi.models[details.collection];
|
: strapi.models[details.collection];
|
||||||
|
|
||||||
const collectionName =
|
// Force singular foreign key
|
||||||
|
details.attribute = singular(details.collection);
|
||||||
|
details.column = targetModel.primaryKey;
|
||||||
|
|
||||||
|
let options = [];
|
||||||
|
if (nature === 'manyWay') {
|
||||||
|
const joinTableName = `${definition.collectionName}__${_.snakeCase(
|
||||||
|
name
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
const foreignKey = `${singular(definition.collectionName)}_${
|
||||||
|
definition.primaryKey
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const otherKey = `${details.attribute}_${details.column}`;
|
||||||
|
|
||||||
|
options = [joinTableName, foreignKey, otherKey];
|
||||||
|
} else {
|
||||||
|
const joinTableName =
|
||||||
_.get(details, 'collectionName') ||
|
_.get(details, 'collectionName') ||
|
||||||
utilsModels.getCollectionName(
|
utilsModels.getCollectionName(
|
||||||
collection.attributes[details.via],
|
targetModel.attributes[details.via],
|
||||||
details
|
details
|
||||||
);
|
);
|
||||||
|
|
||||||
const relationship = collection.attributes[details.via];
|
const relationship = targetModel.attributes[details.via];
|
||||||
|
|
||||||
// Force singular foreign key
|
|
||||||
relationship.attribute = pluralize.singular(relationship.collection);
|
|
||||||
details.attribute = pluralize.singular(details.collection);
|
|
||||||
|
|
||||||
// Define PK column
|
// Define PK column
|
||||||
details.column = utils.getPK(model, strapi.models);
|
relationship.attribute = singular(relationship.collection);
|
||||||
relationship.column = utils.getPK(details.collection, strapi.models);
|
relationship.column = definition.primaryKey;
|
||||||
|
|
||||||
// Sometimes the many-to-many relationships
|
// Sometimes the many-to-many relationships
|
||||||
// is on the same keys on the same models (ex: `friends` key in model `User`)
|
// is on the same keys on the same models (ex: `friends` key in model `User`)
|
||||||
@ -238,31 +249,30 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
`${details.attribute}_${details.column}` ===
|
`${details.attribute}_${details.column}` ===
|
||||||
`${relationship.attribute}_${relationship.column}`
|
`${relationship.attribute}_${relationship.column}`
|
||||||
) {
|
) {
|
||||||
relationship.attribute = pluralize.singular(details.via);
|
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.
|
// Set this info to be able to see if this field is a real database's field.
|
||||||
details.isVirtual = true;
|
details.isVirtual = true;
|
||||||
|
|
||||||
loadedModel[name] = function() {
|
loadedModel[name] = function() {
|
||||||
if (
|
const targetBookshelfModel = GLOBALS[globalId];
|
||||||
_.isArray(_.get(details, 'withPivot')) &&
|
let collection = this.belongsToMany(
|
||||||
!_.isEmpty(details.withPivot)
|
targetBookshelfModel,
|
||||||
) {
|
...options
|
||||||
return this.belongsToMany(
|
);
|
||||||
GLOBALS[globalId],
|
|
||||||
collectionName,
|
if (Array.isArray(details.withPivot)) {
|
||||||
`${relationship.attribute}_${relationship.column}`,
|
return collection.withPivot(details.withPivot);
|
||||||
`${details.attribute}_${details.column}`
|
|
||||||
).withPivot(details.withPivot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.belongsToMany(
|
return collection;
|
||||||
GLOBALS[globalId],
|
|
||||||
collectionName,
|
|
||||||
`${relationship.attribute}_${relationship.column}`,
|
|
||||||
`${details.attribute}_${details.column}`
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,12 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
const { models: { getValuePrimaryKey } } = require('strapi-utils');
|
const {
|
||||||
|
models: { getValuePrimaryKey },
|
||||||
|
} = require('strapi-utils');
|
||||||
|
|
||||||
const transformToArrayID = (array, association) => {
|
const transformToArrayID = (array, association) => {
|
||||||
if(_.isArray(array)) {
|
if (_.isArray(array)) {
|
||||||
array = array.map(value => {
|
array = array.map(value => {
|
||||||
if (_.isPlainObject(value)) {
|
if (_.isPlainObject(value)) {
|
||||||
return value._id || value.id || false;
|
return value._id || value.id || false;
|
||||||
@ -23,7 +25,10 @@ const transformToArrayID = (array, association) => {
|
|||||||
return array.filter(n => n);
|
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]);
|
return _.isEmpty(_.toString(array)) ? [] : transformToArrayID([array]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,19 +36,21 @@ const transformToArrayID = (array, association) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getModel = (model, plugin) => {
|
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));
|
const removeUndefinedKeys = obj => _.pickBy(obj, _.negate(_.isUndefined));
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
findOne: async function (params, populate) {
|
findOne: async function(params, populate) {
|
||||||
const record = await this
|
const record = await this.forge({
|
||||||
.forge({
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
}).fetch({
|
||||||
})
|
withRelated: populate || this.associations.map(x => x.alias),
|
||||||
.fetch({
|
|
||||||
withRelated: populate || this.associations.map(x => x.alias)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = record ? record.toJSON() : record;
|
const data = record ? record.toJSON() : record;
|
||||||
@ -51,11 +58,17 @@ module.exports = {
|
|||||||
// Retrieve data manually.
|
// Retrieve data manually.
|
||||||
if (_.isEmpty(populate)) {
|
if (_.isEmpty(populate)) {
|
||||||
const arrayOfPromises = this.associations
|
const arrayOfPromises = this.associations
|
||||||
.filter(association => ['manyMorphToOne', 'manyMorphToMany'].includes(association.nature))
|
.filter(association =>
|
||||||
|
['manyMorphToOne', 'manyMorphToMany'].includes(association.nature)
|
||||||
|
)
|
||||||
.map(() => {
|
.map(() => {
|
||||||
return this.morph.forge()
|
return this.morph
|
||||||
|
.forge()
|
||||||
.where({
|
.where({
|
||||||
[`${this.collectionName}_id`]: getValuePrimaryKey(params, this.primaryKey)
|
[`${this.collectionName}_id`]: getValuePrimaryKey(
|
||||||
|
params,
|
||||||
|
this.primaryKey
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.fetchAll();
|
.fetchAll();
|
||||||
});
|
});
|
||||||
@ -70,46 +83,73 @@ module.exports = {
|
|||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
update: async function (params) {
|
update: async function(params) {
|
||||||
const relationUpdates = [];
|
const relationUpdates = [];
|
||||||
const primaryKeyValue = getValuePrimaryKey(params, this.primaryKey);
|
const primaryKeyValue = getValuePrimaryKey(params, this.primaryKey);
|
||||||
const response = await module.exports.findOne.call(this, params);
|
const response = await module.exports.findOne.call(this, params);
|
||||||
|
|
||||||
// Only update fields which are on this document.
|
// 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 =
|
||||||
|
params.parseRelationships === false
|
||||||
|
? params.values
|
||||||
|
: Object.keys(removeUndefinedKeys(params.values)).reduce(
|
||||||
|
(acc, current) => {
|
||||||
const property = params.values[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];
|
const details = this._attributes[current];
|
||||||
|
|
||||||
if (!association && _.get(details, 'isVirtual') !== true) {
|
if (!association && _.get(details, 'isVirtual') !== true) {
|
||||||
return _.set(acc, current, property);
|
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) {
|
switch (association.nature) {
|
||||||
case 'oneWay': {
|
case 'oneWay': {
|
||||||
return _.set(acc, current, _.get(property, assocModel.primaryKey, property));
|
return _.set(
|
||||||
|
acc,
|
||||||
|
current,
|
||||||
|
_.get(property, assocModel.primaryKey, property)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case 'oneToOne': {
|
case 'oneToOne': {
|
||||||
if (response[current] === property) return acc;
|
if (response[current] === property) return acc;
|
||||||
|
|
||||||
if (_.isNull(property)) {
|
if (_.isNull(property)) {
|
||||||
const updatePromise = assocModel.where({
|
const updatePromise = assocModel
|
||||||
[assocModel.primaryKey]: getValuePrimaryKey(response[current], assocModel.primaryKey)
|
.where({
|
||||||
}).save({ [details.via]: null }, {method: 'update', patch: true, require: false});
|
[assocModel.primaryKey]: getValuePrimaryKey(
|
||||||
|
response[current],
|
||||||
|
assocModel.primaryKey
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.save(
|
||||||
|
{ [details.via]: null },
|
||||||
|
{ method: 'update', patch: true, require: false }
|
||||||
|
);
|
||||||
|
|
||||||
relationUpdates.push(updatePromise);
|
relationUpdates.push(updatePromise);
|
||||||
return _.set(acc, current, null);
|
return _.set(acc, current, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// set old relations to null
|
// set old relations to null
|
||||||
const updateLink = this.where({ [current]: property })
|
const updateLink = this.where({ [current]: property })
|
||||||
.save({ [current]: null }, {method: 'update', patch: true, require: false})
|
.save(
|
||||||
|
{ [current]: null },
|
||||||
|
{ method: 'update', patch: true, require: false }
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return assocModel
|
return assocModel
|
||||||
.where({ [this.primaryKey]: property })
|
.where({ [this.primaryKey]: property })
|
||||||
.save({ [details.via] : primaryKeyValue}, {method: 'update', patch: true, require: false});
|
.save(
|
||||||
|
{ [details.via]: primaryKeyValue },
|
||||||
|
{ method: 'update', patch: true, require: false }
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// set new relation
|
// set new relation
|
||||||
@ -121,33 +161,67 @@ module.exports = {
|
|||||||
|
|
||||||
// set relation to null for all the ids not in the list
|
// set relation to null for all the ids not in the list
|
||||||
const currentIds = response[current];
|
const currentIds = response[current];
|
||||||
const toRemove = _.differenceWith(currentIds, property, (a, b) => {
|
const toRemove = _.differenceWith(
|
||||||
return `${a[assocModel.primaryKey] || a}` === `${b[assocModel.primaryKey] || b}`;
|
currentIds,
|
||||||
});
|
property,
|
||||||
|
(a, b) => {
|
||||||
|
return (
|
||||||
|
`${a[assocModel.primaryKey] || a}` ===
|
||||||
|
`${b[assocModel.primaryKey] || b}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const updatePromise = assocModel
|
const updatePromise = assocModel
|
||||||
.where(assocModel.primaryKey, 'in', toRemove.map(val => val[assocModel.primaryKey]||val))
|
.where(
|
||||||
.save({ [details.via] : null }, { method: 'update', patch: true, require: false })
|
assocModel.primaryKey,
|
||||||
|
'in',
|
||||||
|
toRemove.map(val => val[assocModel.primaryKey] || val)
|
||||||
|
)
|
||||||
|
.save(
|
||||||
|
{ [details.via]: null },
|
||||||
|
{ method: 'update', patch: true, require: false }
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return assocModel
|
return assocModel
|
||||||
.where(assocModel.primaryKey, 'in', property.map(val => val[assocModel.primaryKey]||val))
|
.where(
|
||||||
.save({ [details.via] : primaryKeyValue }, { method: 'update', patch: true, require: false });
|
assocModel.primaryKey,
|
||||||
|
'in',
|
||||||
|
property.map(val => val[assocModel.primaryKey] || val)
|
||||||
|
)
|
||||||
|
.save(
|
||||||
|
{ [details.via]: primaryKeyValue },
|
||||||
|
{ method: 'update', patch: true, require: false }
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
relationUpdates.push(updatePromise);
|
relationUpdates.push(updatePromise);
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
case 'manyToOne': {
|
case 'manyToOne': {
|
||||||
return _.set(acc, current, _.get(property, assocModel.primaryKey, property));
|
return _.set(
|
||||||
|
acc,
|
||||||
|
current,
|
||||||
|
_.get(property, assocModel.primaryKey, property)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
case 'manyWay':
|
||||||
case 'manyToMany': {
|
case 'manyToMany': {
|
||||||
const currentValue = transformToArrayID(response[current], association).map(id => id.toString());
|
const currentValue = transformToArrayID(
|
||||||
const storedValue = transformToArrayID(params.values[current], association).map(id => id.toString());
|
response[current],
|
||||||
|
association
|
||||||
|
).map(id => id.toString());
|
||||||
|
const storedValue = transformToArrayID(
|
||||||
|
params.values[current],
|
||||||
|
association
|
||||||
|
).map(id => id.toString());
|
||||||
|
|
||||||
const toAdd = _.difference(storedValue, currentValue);
|
const toAdd = _.difference(storedValue, currentValue);
|
||||||
const toRemove = _.difference(currentValue, storedValue);
|
const toRemove = _.difference(currentValue, storedValue);
|
||||||
|
|
||||||
const collection = this.forge({ [this.primaryKey]: primaryKeyValue })[association.alias]();
|
const collection = this.forge({
|
||||||
|
[this.primaryKey]: primaryKeyValue,
|
||||||
|
})[association.alias]();
|
||||||
const updatePromise = collection
|
const updatePromise = collection
|
||||||
.detach(toRemove)
|
.detach(toRemove)
|
||||||
.then(() => collection.attach(toAdd));
|
.then(() => collection.attach(toAdd));
|
||||||
@ -159,19 +233,21 @@ module.exports = {
|
|||||||
case 'manyMorphToOne':
|
case 'manyMorphToOne':
|
||||||
// Update the relational array.
|
// Update the relational array.
|
||||||
params.values[current].forEach(obj => {
|
params.values[current].forEach(obj => {
|
||||||
const model = obj.source && obj.source !== 'content-manager' ?
|
const model =
|
||||||
strapi.plugins[obj.source].models[obj.ref]:
|
obj.source && obj.source !== 'content-manager'
|
||||||
strapi.models[obj.ref];
|
? strapi.plugins[obj.source].models[obj.ref]
|
||||||
|
: strapi.models[obj.ref];
|
||||||
|
|
||||||
// Remove existing relationship because only one file
|
// Remove existing relationship because only one file
|
||||||
// can be related to this field.
|
// can be related to this field.
|
||||||
if (association.nature === 'manyMorphToOne') {
|
if (association.nature === 'manyMorphToOne') {
|
||||||
relationUpdates.push(
|
relationUpdates.push(
|
||||||
module.exports.removeRelationMorph.call(this, {
|
module.exports.removeRelationMorph
|
||||||
|
.call(this, {
|
||||||
alias: association.alias,
|
alias: association.alias,
|
||||||
ref: model.collectionName,
|
ref: model.collectionName,
|
||||||
refId: obj.refId,
|
refId: obj.refId,
|
||||||
field: obj.field
|
field: obj.field,
|
||||||
})
|
})
|
||||||
.then(() =>
|
.then(() =>
|
||||||
module.exports.addRelationMorph.call(this, {
|
module.exports.addRelationMorph.call(this, {
|
||||||
@ -179,31 +255,42 @@ module.exports = {
|
|||||||
alias: association.alias,
|
alias: association.alias,
|
||||||
ref: model.collectionName,
|
ref: model.collectionName,
|
||||||
refId: obj.refId,
|
refId: obj.refId,
|
||||||
field: obj.field
|
field: obj.field,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
relationUpdates.push(module.exports.addRelationMorph.call(this, {
|
relationUpdates.push(
|
||||||
|
module.exports.addRelationMorph.call(this, {
|
||||||
id: response[this.primaryKey],
|
id: response[this.primaryKey],
|
||||||
alias: association.alias,
|
alias: association.alias,
|
||||||
ref: model.collectionName,
|
ref: model.collectionName,
|
||||||
refId: obj.refId,
|
refId: obj.refId,
|
||||||
field: obj.field
|
field: obj.field,
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'oneToManyMorph':
|
case 'oneToManyMorph':
|
||||||
case 'manyToManyMorph': {
|
case 'manyToManyMorph': {
|
||||||
// Compare array of ID to find deleted files.
|
// Compare array of ID to find deleted files.
|
||||||
const currentValue = transformToArrayID(response[current], association).map(id => id.toString());
|
const currentValue = transformToArrayID(
|
||||||
const storedValue = transformToArrayID(params.values[current], association).map(id => id.toString());
|
response[current],
|
||||||
|
association
|
||||||
|
).map(id => id.toString());
|
||||||
|
const storedValue = transformToArrayID(
|
||||||
|
params.values[current],
|
||||||
|
association
|
||||||
|
).map(id => id.toString());
|
||||||
|
|
||||||
const toAdd = _.difference(storedValue, currentValue);
|
const toAdd = _.difference(storedValue, currentValue);
|
||||||
const toRemove = _.difference(currentValue, storedValue);
|
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 => {
|
toAdd.forEach(id => {
|
||||||
relationUpdates.push(
|
relationUpdates.push(
|
||||||
@ -212,7 +299,7 @@ module.exports = {
|
|||||||
alias: association.via,
|
alias: association.via,
|
||||||
ref: this.collectionName,
|
ref: this.collectionName,
|
||||||
refId: response.id,
|
refId: response.id,
|
||||||
field: association.alias
|
field: association.alias,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -225,7 +312,7 @@ module.exports = {
|
|||||||
alias: association.via,
|
alias: association.via,
|
||||||
ref: this.collectionName,
|
ref: this.collectionName,
|
||||||
refId: response.id,
|
refId: response.id,
|
||||||
field: association.alias
|
field: association.alias,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -238,16 +325,17 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
delete values[this.primaryKey];
|
||||||
if (!_.isEmpty(values)) {
|
if (!_.isEmpty(values)) {
|
||||||
relationUpdates.push(
|
relationUpdates.push(
|
||||||
this
|
this.forge({
|
||||||
.forge({
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
}).save(values, {
|
||||||
})
|
patch: true,
|
||||||
.save(values, {
|
|
||||||
patch: true
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -257,17 +345,17 @@ module.exports = {
|
|||||||
// Update virtuals fields.
|
// Update virtuals fields.
|
||||||
await Promise.all(relationUpdates);
|
await Promise.all(relationUpdates);
|
||||||
|
|
||||||
return await this
|
return await this.forge({
|
||||||
.forge({
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
}).fetch({
|
||||||
})
|
withRelated: this.associations.map(x => x.alias),
|
||||||
.fetch({
|
|
||||||
withRelated: this.associations.map(x => x.alias)
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addRelation: async function (params) {
|
addRelation: async function(params) {
|
||||||
const association = this.associations.find(x => x.via === params.foreignKey && _.get(params.values, x.alias, null));
|
const association = this.associations.find(
|
||||||
|
x => x.via === params.foreignKey && _.get(params.values, x.alias, null)
|
||||||
|
);
|
||||||
|
|
||||||
if (!association) {
|
if (!association) {
|
||||||
// Resolve silently.
|
// Resolve silently.
|
||||||
@ -281,16 +369,20 @@ module.exports = {
|
|||||||
return module.exports.update.call(this, params);
|
return module.exports.update.call(this, params);
|
||||||
case 'manyToMany':
|
case 'manyToMany':
|
||||||
return this.forge({
|
return this.forge({
|
||||||
[this.primaryKey]: params[this.primaryKey]
|
[this.primaryKey]: params[this.primaryKey],
|
||||||
})[association.alias]().attach(params.values[association.alias]);
|
})
|
||||||
|
[association.alias]()
|
||||||
|
.attach(params.values[association.alias]);
|
||||||
default:
|
default:
|
||||||
// Resolve silently.
|
// Resolve silently.
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeRelation: async function (params) {
|
removeRelation: async function(params) {
|
||||||
const association = this.associations.find(x => x.via === params.foreignKey && _.get(params.values, x.alias, null));
|
const association = this.associations.find(
|
||||||
|
x => x.via === params.foreignKey && _.get(params.values, x.alias, null)
|
||||||
|
);
|
||||||
|
|
||||||
if (!association) {
|
if (!association) {
|
||||||
// Resolve silently.
|
// Resolve silently.
|
||||||
@ -304,24 +396,27 @@ module.exports = {
|
|||||||
return module.exports.update.call(this, params);
|
return module.exports.update.call(this, params);
|
||||||
case 'manyToMany':
|
case 'manyToMany':
|
||||||
return this.forge({
|
return this.forge({
|
||||||
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey)
|
[this.primaryKey]: getValuePrimaryKey(params, this.primaryKey),
|
||||||
})[association.alias]().detach(params.values[association.alias]);
|
})
|
||||||
|
[association.alias]()
|
||||||
|
.detach(params.values[association.alias]);
|
||||||
default:
|
default:
|
||||||
// Resolve silently.
|
// Resolve silently.
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addRelationMorph: async function (params) {
|
addRelationMorph: async function(params) {
|
||||||
const record = await this.morph.forge()
|
const record = await this.morph
|
||||||
|
.forge()
|
||||||
.where({
|
.where({
|
||||||
[`${this.collectionName}_id`]: params.id,
|
[`${this.collectionName}_id`]: params.id,
|
||||||
[`${params.alias}_id`]: params.refId,
|
[`${params.alias}_id`]: params.refId,
|
||||||
[`${params.alias}_type`]: params.ref,
|
[`${params.alias}_type`]: params.ref,
|
||||||
field: params.field
|
field: params.field,
|
||||||
})
|
})
|
||||||
.fetch({
|
.fetch({
|
||||||
withRelated: this.associations.map(x => x.alias)
|
withRelated: this.associations.map(x => x.alias),
|
||||||
});
|
});
|
||||||
|
|
||||||
const entry = record ? record.toJSON() : record;
|
const entry = record ? record.toJSON() : record;
|
||||||
@ -330,25 +425,32 @@ module.exports = {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.morph.forge({
|
return await this.morph
|
||||||
|
.forge({
|
||||||
[`${this.collectionName}_id`]: params.id,
|
[`${this.collectionName}_id`]: params.id,
|
||||||
[`${params.alias}_id`]: params.refId,
|
[`${params.alias}_id`]: params.refId,
|
||||||
[`${params.alias}_type`]: params.ref,
|
[`${params.alias}_type`]: params.ref,
|
||||||
field: params.field
|
field: params.field,
|
||||||
})
|
})
|
||||||
.save();
|
.save();
|
||||||
},
|
},
|
||||||
|
|
||||||
removeRelationMorph: async function (params) {
|
removeRelationMorph: async function(params) {
|
||||||
return await this.morph.forge()
|
return await this.morph
|
||||||
.where(_.omitBy({
|
.forge()
|
||||||
|
.where(
|
||||||
|
_.omitBy(
|
||||||
|
{
|
||||||
[`${this.collectionName}_id`]: params.id,
|
[`${this.collectionName}_id`]: params.id,
|
||||||
[`${params.alias}_id`]: params.refId,
|
[`${params.alias}_id`]: params.refId,
|
||||||
[`${params.alias}_type`]: params.ref,
|
[`${params.alias}_type`]: params.ref,
|
||||||
field: params.field
|
field: params.field,
|
||||||
}, _.isUndefined))
|
},
|
||||||
|
_.isUndefined
|
||||||
|
)
|
||||||
|
)
|
||||||
.destroy({
|
.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
|
* Module dependencies
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Node.js core
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
// Public node modules.
|
// Public node modules.
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const pluralize = require('pluralize');
|
const pluralize = require('pluralize');
|
||||||
@ -29,33 +26,6 @@ module.exports = {
|
|||||||
cb();
|
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
|
* Retrieve the value based on the primary key
|
||||||
*/
|
*/
|
||||||
@ -64,34 +34,6 @@ module.exports = {
|
|||||||
return value[defaultKey] || value.id || value._id;
|
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
|
* Find relation nature with verbose
|
||||||
*/
|
*/
|
||||||
@ -104,11 +46,14 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (_.isUndefined(models)) {
|
if (_.isUndefined(models)) {
|
||||||
models = association.plugin ? strapi.plugins[association.plugin].models : strapi.models;
|
models = association.plugin
|
||||||
|
? strapi.plugins[association.plugin].models
|
||||||
|
: strapi.models;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(association.hasOwnProperty('collection') && association.collection === '*') ||
|
(association.hasOwnProperty('collection') &&
|
||||||
|
association.collection === '*') ||
|
||||||
(association.hasOwnProperty('model') && association.model === '*')
|
(association.hasOwnProperty('model') && association.model === '*')
|
||||||
) {
|
) {
|
||||||
if (association.model) {
|
if (association.model) {
|
||||||
@ -117,20 +62,28 @@ module.exports = {
|
|||||||
types.current = 'morphTo';
|
types.current = 'morphTo';
|
||||||
}
|
}
|
||||||
|
|
||||||
const flattenedPluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
|
const flattenedPluginsModels = Object.keys(strapi.plugins).reduce(
|
||||||
|
(acc, current) => {
|
||||||
Object.keys(strapi.plugins[current].models).forEach(model => {
|
Object.keys(strapi.plugins[current].models).forEach(model => {
|
||||||
acc[`${current}_${model}`] = strapi.plugins[current].models[model];
|
acc[`${current}_${model}`] =
|
||||||
|
strapi.plugins[current].models[model];
|
||||||
});
|
});
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
const allModels = _.merge({}, strapi.models, flattenedPluginsModels);
|
const allModels = _.merge({}, strapi.models, flattenedPluginsModels);
|
||||||
|
|
||||||
// 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 key
|
||||||
_.forIn(allModels, model => {
|
_.forIn(allModels, model => {
|
||||||
_.forIn(model.attributes, attribute => {
|
_.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')) {
|
if (attribute.hasOwnProperty('collection')) {
|
||||||
types.other = 'collection';
|
types.other = 'collection';
|
||||||
|
|
||||||
@ -145,14 +98,22 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
|
} else if (
|
||||||
const relatedAttribute = models[association.collection].attributes[association.via];
|
association.hasOwnProperty('via') &&
|
||||||
|
association.hasOwnProperty('collection')
|
||||||
|
) {
|
||||||
|
const relatedAttribute =
|
||||||
|
models[association.collection].attributes[association.via];
|
||||||
|
|
||||||
if (!relatedAttribute) {
|
if (!relatedAttribute) {
|
||||||
throw new Error(
|
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 + ')' : ''
|
association.plugin ? '(plugin - ' + association.plugin + ')' : ''
|
||||||
}`,
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,12 +131,21 @@ module.exports = {
|
|||||||
!relatedAttribute.hasOwnProperty('via')
|
!relatedAttribute.hasOwnProperty('via')
|
||||||
) {
|
) {
|
||||||
types.other = 'collectionD';
|
types.other = 'collectionD';
|
||||||
} else if (relatedAttribute.hasOwnProperty('model') && relatedAttribute.model !== '*') {
|
} else if (
|
||||||
|
relatedAttribute.hasOwnProperty('model') &&
|
||||||
|
relatedAttribute.model !== '*'
|
||||||
|
) {
|
||||||
types.other = 'model';
|
types.other = 'model';
|
||||||
} else if (relatedAttribute.hasOwnProperty('collection') || relatedAttribute.hasOwnProperty('model')) {
|
} else if (
|
||||||
|
relatedAttribute.hasOwnProperty('collection') ||
|
||||||
|
relatedAttribute.hasOwnProperty('model')
|
||||||
|
) {
|
||||||
types.other = 'morphTo';
|
types.other = 'morphTo';
|
||||||
}
|
}
|
||||||
} else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) {
|
} else if (
|
||||||
|
association.hasOwnProperty('via') &&
|
||||||
|
association.hasOwnProperty('model')
|
||||||
|
) {
|
||||||
types.current = 'modelD';
|
types.current = 'modelD';
|
||||||
|
|
||||||
// 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 key
|
||||||
@ -189,9 +159,15 @@ module.exports = {
|
|||||||
attribute.collection !== '*'
|
attribute.collection !== '*'
|
||||||
) {
|
) {
|
||||||
types.other = 'collection';
|
types.other = 'collection';
|
||||||
} else if (attribute.hasOwnProperty('model') && attribute.model !== '*') {
|
} else if (
|
||||||
|
attribute.hasOwnProperty('model') &&
|
||||||
|
attribute.model !== '*'
|
||||||
|
) {
|
||||||
types.other = 'model';
|
types.other = 'model';
|
||||||
} else if (attribute.hasOwnProperty('collection') || attribute.hasOwnProperty('model')) {
|
} else if (
|
||||||
|
attribute.hasOwnProperty('collection') ||
|
||||||
|
attribute.hasOwnProperty('model')
|
||||||
|
) {
|
||||||
types.other = 'morphTo';
|
types.other = 'morphTo';
|
||||||
}
|
}
|
||||||
} else if (association.hasOwnProperty('model')) {
|
} else if (association.hasOwnProperty('model')) {
|
||||||
@ -268,14 +244,18 @@ module.exports = {
|
|||||||
nature: 'oneMorphToOne',
|
nature: 'oneMorphToOne',
|
||||||
verbose: 'belongsToMorph',
|
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 {
|
return {
|
||||||
nature: 'manyMorphToOne',
|
nature: 'manyMorphToOne',
|
||||||
verbose: 'belongsToManyMorph',
|
verbose: 'belongsToManyMorph',
|
||||||
};
|
};
|
||||||
} else if (
|
} else if (
|
||||||
types.current === 'morphTo' &&
|
types.current === 'morphTo' &&
|
||||||
(types.other === 'collection' || association.hasOwnProperty('collection'))
|
(types.other === 'collection' ||
|
||||||
|
association.hasOwnProperty('collection'))
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
nature: 'manyMorphToMany',
|
nature: 'manyMorphToMany',
|
||||||
@ -291,7 +271,10 @@ module.exports = {
|
|||||||
nature: 'oneToOne',
|
nature: 'oneToOne',
|
||||||
verbose: 'hasOne',
|
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 {
|
return {
|
||||||
nature: 'manyToOne',
|
nature: 'manyToOne',
|
||||||
verbose: 'belongsTo',
|
verbose: 'belongsTo',
|
||||||
@ -306,7 +289,10 @@ module.exports = {
|
|||||||
nature: 'oneToMany',
|
nature: 'oneToMany',
|
||||||
verbose: 'hasMany',
|
verbose: 'hasMany',
|
||||||
};
|
};
|
||||||
} else if (types.current === 'collection' && types.other === 'collection') {
|
} else if (
|
||||||
|
types.current === 'collection' &&
|
||||||
|
types.other === 'collection'
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
nature: 'manyToMany',
|
nature: 'manyToMany',
|
||||||
verbose: 'belongsToMany',
|
verbose: 'belongsToMany',
|
||||||
@ -334,21 +320,15 @@ module.exports = {
|
|||||||
return undefined;
|
return undefined;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
strapi.log.error(
|
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.log.error(e);
|
||||||
strapi.stop();
|
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
|
* Return table name for a collection many-to-many
|
||||||
*/
|
*/
|
||||||
@ -363,9 +343,7 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
.map(table =>
|
.map(table =>
|
||||||
_.snakeCase(
|
_.snakeCase(
|
||||||
`${pluralize.plural(table.collection)} ${pluralize.plural(
|
`${pluralize.plural(table.collection)} ${pluralize.plural(table.via)}`
|
||||||
table.via
|
|
||||||
)}`
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.join('__');
|
.join('__');
|
||||||
@ -383,23 +361,44 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exclude non-relational attribute
|
// Exclude non-relational attribute
|
||||||
if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) {
|
if (!_.has(association, 'collection') && !_.has(association, 'model')) {
|
||||||
return undefined;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get relation nature
|
// Get relation nature
|
||||||
let details;
|
let details;
|
||||||
const globalName = association.model || association.collection || '';
|
const targetName = association.model || association.collection || '';
|
||||||
const infos = this.getNature(association, key, undefined, model.toLowerCase());
|
const infos = this.getNature(
|
||||||
|
association,
|
||||||
|
key,
|
||||||
|
undefined,
|
||||||
|
model.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
if (globalName !== '*') {
|
if (targetName !== '*') {
|
||||||
details = association.plugin
|
if (association.plugin) {
|
||||||
? _.get(strapi.plugins, `${association.plugin}.models.${globalName}.attributes.${association.via}`, {})
|
details = _.get(
|
||||||
: _.get(strapi.models, `${globalName}.attributes.${association.via}`, {});
|
strapi.plugins,
|
||||||
|
[
|
||||||
|
association.plugin,
|
||||||
|
'models',
|
||||||
|
targetName,
|
||||||
|
'attributes',
|
||||||
|
association.via,
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
details = _.get(
|
||||||
|
strapi.models,
|
||||||
|
[targetName, 'attributes', association.via],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build associations object
|
// Build associations object
|
||||||
if (association.hasOwnProperty('collection') && association.collection !== '*') {
|
if (_.has(association, 'collection') && association.collection !== '*') {
|
||||||
const ast = {
|
const ast = {
|
||||||
alias: key,
|
alias: key,
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
@ -413,11 +412,22 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (infos.nature === 'manyToMany' && definition.orm === 'bookshelf') {
|
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);
|
definition.associations.push(ast);
|
||||||
} else if (association.hasOwnProperty('model') && association.model !== '*') {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.has(association, 'model') && association.model !== '*') {
|
||||||
definition.associations.push({
|
definition.associations.push({
|
||||||
alias: key,
|
alias: key,
|
||||||
type: 'model',
|
type: 'model',
|
||||||
@ -429,15 +439,23 @@ module.exports = {
|
|||||||
plugin: association.plugin || undefined,
|
plugin: association.plugin || undefined,
|
||||||
filter: details.filter,
|
filter: details.filter,
|
||||||
});
|
});
|
||||||
} else if (association.hasOwnProperty('collection') || association.hasOwnProperty('model')) {
|
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).forEach(entity => {
|
||||||
Object.keys(strapi.plugins[current].models[entity].attributes).forEach(attribute => {
|
Object.keys(
|
||||||
const attr = strapi.plugins[current].models[entity].attributes[attribute];
|
strapi.plugins[current].models[entity].attributes
|
||||||
|
).forEach(attribute => {
|
||||||
|
const attr =
|
||||||
|
strapi.plugins[current].models[entity].attributes[attribute];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
|
(attr.collection || attr.model || '').toLowerCase() ===
|
||||||
strapi.plugins[current].models[entity].globalId !== definition.globalId
|
model.toLowerCase() &&
|
||||||
|
strapi.plugins[current].models[entity].globalId !==
|
||||||
|
definition.globalId
|
||||||
) {
|
) {
|
||||||
acc.push(strapi.plugins[current].models[entity].globalId);
|
acc.push(strapi.plugins[current].models[entity].globalId);
|
||||||
}
|
}
|
||||||
@ -445,14 +463,17 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const appModels = Object.keys(strapi.models).reduce((acc, entity) => {
|
const appModels = Object.keys(strapi.models).reduce((acc, entity) => {
|
||||||
Object.keys(strapi.models[entity].attributes).forEach(attribute => {
|
Object.keys(strapi.models[entity].attributes).forEach(attribute => {
|
||||||
const attr = strapi.models[entity].attributes[attribute];
|
const attr = strapi.models[entity].attributes[attribute];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
|
(attr.collection || attr.model || '').toLowerCase() ===
|
||||||
|
model.toLowerCase() &&
|
||||||
strapi.models[entity].globalId !== definition.globalId
|
strapi.models[entity].globalId !== definition.globalId
|
||||||
) {
|
) {
|
||||||
acc.push(strapi.models[entity].globalId);
|
acc.push(strapi.models[entity].globalId);
|
||||||
@ -472,21 +493,22 @@ module.exports = {
|
|||||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||||
filter: association.filter,
|
filter: association.filter,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} catch (e) {
|
} 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.log.error(e);
|
||||||
strapi.stop();
|
strapi.stop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getVia: (attribute, association) => {
|
|
||||||
return _.findKey(strapi.models[association.model || association.collection].attributes, { via: attribute });
|
|
||||||
},
|
|
||||||
|
|
||||||
convertParams: (entity, params) => {
|
convertParams: (entity, params) => {
|
||||||
if (!entity) {
|
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
|
// 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) => {
|
Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||||
_.assign(acc, _.get(strapi.plugins[current], ['models'], {}));
|
_.assign(acc, _.get(strapi.plugins[current], ['models'], {}));
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!models.hasOwnProperty(model)) {
|
if (!models.hasOwnProperty(model)) {
|
||||||
@ -513,7 +535,9 @@ module.exports = {
|
|||||||
const connector = models[model].orm;
|
const connector = models[model].orm;
|
||||||
|
|
||||||
if (!connector) {
|
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;
|
const convertor = strapi.hook[connector].load().getQueryParams;
|
||||||
@ -555,13 +579,31 @@ module.exports = {
|
|||||||
} else {
|
} else {
|
||||||
const suffix = key.split('_');
|
const suffix = key.split('_');
|
||||||
// Mysql stores boolean as 1 or 0
|
// 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';
|
formattedValue = value.toString() === 'true' ? '1' : '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
let type;
|
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)}`;
|
type = `_${_.last(suffix)}`;
|
||||||
key = _.dropRight(suffix).join('_');
|
key = _.dropRight(suffix).join('_');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user