Add config id type

This commit is contained in:
KuongKnight 2018-05-21 17:22:25 +07:00 committed by GitHub
parent 6b2e1f2bde
commit 4a3c25fbfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -12,12 +12,11 @@ const _ = require('lodash');
const bookshelf = require('bookshelf');
const pluralize = require('pluralize');
// Strapi helpers for models.
const utilsModels = require('strapi-utils').models;
// Local helpers.
const utils = require('./utils/');
const relations = require('./relations');
// Strapi helpers for models.
const utilsModels = require('strapi-utils').models;
const PIVOT_PREFIX = '_pivot_';
const GLOBALS = {};
@ -26,11 +25,8 @@ const GLOBALS = {};
* Bookshelf hook
*/
/* eslint-disable no-unused-vars */
/* eslint-disable prefer-template */
/* eslint-disable no-case-declarations */
module.exports = function(strapi) {
const hook = _.merge({
const hook = {
/**
* Default options
*/
@ -79,17 +75,16 @@ module.exports = function(strapi) {
_.forEach(models, (definition, model) => {
definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
_.defaults(definition, {
primaryKey: 'id'
});
// Define local GLOBALS to expose every models in this file.
GLOBALS[definition.globalId] = {};
// Add some informations about ORM & client connection & tableName
definition.orm = 'bookshelf';
definition.client = _.get(connection.settings, 'client');
_.defaults(definition, {
primaryKey: 'id',
primaryKeyType: _.get(definition, 'options.idAttributeType', definition.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT')
});
// Register the final model for Bookshelf.
const loadedModel = _.assign({
tableName: definition.collectionName,
@ -317,7 +312,6 @@ module.exports = function(strapi) {
// Push attributes to be aware of model schema.
target[model]._attributes = definition.attributes;
target[model].updateRelations = relations.update;
databaseUpdate.push(new Promise(async (resolve) => {
// Equilize database tables
@ -336,19 +330,23 @@ module.exports = function(strapi) {
switch (relation.nature) {
case 'oneToOne':
case 'manyToOne':
type = definition.client === 'pg' ? 'integer' : 'int';
type = definition.primaryKeyType;
break;
default:
return null;
}
} else {
switch (attribute.type) {
case 'uuid':
type = definition.client === 'pg' ? 'uuid' : 'varchar(255)';
break;
case 'text':
case 'json':
type = 'text';
break;
case 'json':
type = definition.client === 'pg' ? 'jsonb' : 'text';
break;
case 'string':
case 'enumeration':
case 'password':
case 'email':
type = 'varchar(255)';
@ -358,10 +356,8 @@ module.exports = function(strapi) {
type = definition.client === 'pg' ? 'integer' : 'int';
break;
case 'float':
type = definition.client === 'pg' ? 'double precision' : 'double';
break;
case 'decimal':
type = 'decimal';
type = attribute.type;
break;
case 'date':
case 'time':
@ -398,7 +394,11 @@ module.exports = function(strapi) {
};
if (!tableExist) {
const columns = generateColumns(attributes, [`id ${definition.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY`]).join(',\n\r');
let idAttributeBuilder = [`id ${definition.primaryKeyType} NOT NULL PRIMARY KEY`];
if (definition.primaryKeyType === 'uuid' && definition.client === 'pg') {
idAttributeBuilder = ['id uuid NOT NULL DEFAULT uuid_generate_v4() NOT NULL PRIMARY KEY'];
}
const columns = generateColumns(attributes, idAttributeBuilder).join(',\n\r');
// Create table
await ORM.knex.raw(`
@ -483,10 +483,10 @@ module.exports = function(strapi) {
if (morphRelations) {
const attributes = {
[`${loadedModel.tableName}_id`]: {
type: 'integer'
type: definition.primaryKeyType
},
[`${morphRelations.alias}_id`]: {
type: 'integer'
type: definition.primaryKeyType
},
[`${morphRelations.alias}_type`]: {
type: 'text'
@ -511,10 +511,10 @@ module.exports = function(strapi) {
const attributes = {
[`${pluralize.singular(manyRelations.collection)}_id`]: {
type: 'integer'
type: definition.primaryKeyType
},
[`${pluralize.singular(definition.globalId.toLowerCase())}_id`]: {
type: 'integer'
type: definition.primaryKeyType
}
};
@ -849,7 +849,7 @@ module.exports = function(strapi) {
cb();
},
getQueryParams: (value, type, key) => {
getQueryParams: (value, type, key) => {
const result = {};
switch (type) {
@ -896,18 +896,18 @@ module.exports = function(strapi) {
};
break;
case '_sort':
result.key = `sort`;
result.key = 'sort';
result.value = {
key,
order: value.toUpperCase()
};
break;
case '_start':
result.key = `start`;
result.key = 'start';
result.value = parseFloat(value);
break;
case '_limit':
result.key = `limit`;
result.key = 'limit';
result.value = parseFloat(value);
break;
case '_contains':
@ -923,8 +923,340 @@ module.exports = function(strapi) {
}
return result;
},
manageRelations: async function (model, params) {
const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
_.assign(acc, strapi.plugins[current].models);
return acc;
}, {}));
const Model = models[model];
const virtualFields = [];
const record = await Model
.forge({
[Model.primaryKey]: params[Model.primaryKey]
})
.fetch({
withRelated: Model.associations.map(x => x.alias)
});
const response = record ? record.toJSON() : record;
// Only update fields which are on this document.
const values = params.parseRelationships === false ? params.values : Object.keys(JSON.parse(JSON.stringify(params.values))).reduce((acc, current) => {
const association = Model.associations.filter(x => x.alias === current)[0];
const details = Model._attributes[current];
if (_.get(Model._attributes, `${current}.isVirtual`) !== true && _.isUndefined(association)) {
acc[current] = params.values[current];
} else {
switch (association.nature) {
case 'oneWay':
acc[current] = _.get(params.values[current], this.primaryKey, params.values[current]) || null;
break;
case 'oneToOne':
if (response[current] !== params.values[current]) {
const value = _.isNull(params.values[current]) ? response[current] : params.values;
const recordId = _.isNull(params.values[current]) ? value[Model.primaryKey] || value.id || value._id : typeof value[current] === 'object' ? value[current].id : value[current];
if (response[current] && _.isObject(response[current]) && response[current][Model.primaryKey] !== value[current]) {
virtualFields.push(
this.manageRelations(details.collection || details.model, {
id: response[current][Model.primaryKey],
values: {
[details.via]: null
},
parseRelationships: false
})
);
}
// Remove previous relationship asynchronously if it exists.
virtualFields.push(
models[details.model || details.collection]
.forge({ id : recordId })
.fetch({
withRelated: models[details.model || details.collection].associations.map(x => x.alias)
})
.then(response => {
const record = response ? response.toJSON() : response;
if (record && _.isObject(record[details.via]) && record[details.via][current] !== value[current]) {
return this.manageRelations(model, {
id: record[details.via][models[details.model || details.collection].primaryKey] || record[details.via].id,
values: {
[current]: null
},
parseRelationships: false
});
}
return Promise.resolve();
})
);
// Update the record on the other side.
// When params.values[current] is null this means that we are removing the relation.
virtualFields.push(this.manageRelations(details.model || details.collection, {
id: recordId,
values: {
[details.via]: _.isNull(params.values[current]) ? null : value[Model.primaryKey] || params.id || params._id || value.id || value._id
},
parseRelationships: false
}));
acc[current] = _.isNull(params.values[current]) ? null : typeof value[current] === 'object' ? value[current][Model.primaryKey] : value[current];
}
break;
case 'oneToMany':
case 'manyToOne':
case 'manyToMany':
if (details.dominant === true) {
acc[current] = params.values[current];
} else if (response[current] && _.isArray(response[current]) && current !== 'id') {
// Records to add in the relation.
const toAdd = _.differenceWith(params.values[current], response[current], (a, b) =>
((typeof a === 'number') ? a : a[Model.primaryKey].toString()) === b[Model.primaryKey].toString()
);
// Records to remove in the relation.
const toRemove = _.differenceWith(response[current], params.values[current], (a, b) =>
a[Model.primaryKey].toString() === ((typeof b === 'number') ? b : b[Model.primaryKey].toString())
)
.filter(x => toAdd.find(y => x.id === y.id) === undefined);
// Push the work into the flow process.
toAdd.forEach(value => {
value = (typeof value === 'number') ? { id: value } : value;
value[details.via] = parseFloat(params[Model.primaryKey]);
params.values[Model.primaryKey] = parseFloat(params[Model.primaryKey]);
virtualFields.push(this.addRelation(details.model || details.collection, {
id: value[Model.primaryKey] || value.id || value._id,
values: association.nature === 'manyToMany' ? params.values : value,
foreignKey: current
}, details.plugin));
});
toRemove.forEach(value => {
value[details.via] = null;
virtualFields.push(this.removeRelation(details.model || details.collection, {
id: value[Model.primaryKey] || value.id || value._id,
values: association.nature === 'manyToMany' ? params.values : value,
foreignKey: current
}, details.plugin));
});
} else if (_.get(Model._attributes, `${current}.isVirtual`) !== true) {
acc[current] = params.values[current];
}
break;
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];
virtualFields.push(this.addRelationMorph(details.model || details.collection, {
id: response[this.primaryKey],
alias: association.alias,
ref: model.collectionName,
refId: obj.refId,
field: obj.field
}, obj.source));
});
break;
case 'oneToManyMorph':
case 'manyToManyMorph':
const transformToArrayID = (array) => {
if(_.isArray(array)) {
return array.map(value => {
if (_.isPlainObject(value)) {
return value._id || value.id;
}
return value;
});
}
if (association.type === 'model') {
return _.isEmpty(array) ? [] : transformToArrayID([array]);
}
return [];
};
// Compare array of ID to find deleted files.
const currentValue = transformToArrayID(response[current]).map(id => id.toString());
const storedValue = transformToArrayID(params.values[current]).map(id => id.toString());
const toAdd = _.difference(storedValue, currentValue);
const toRemove = _.difference(currentValue, storedValue);
toAdd.forEach(id => {
virtualFields.push(this.addRelationMorph(details.model || details.collection, {
id,
alias: association.via,
ref: Model.collectionName,
refId: response.id,
field: association.alias
}, details.plugin));
});
// Update the relational array.
toRemove.forEach(id => {
virtualFields.push(this.removeRelationMorph(details.model || details.collection, {
id,
alias: association.via,
ref: Model.collectionName,
refId: response.id,
field: association.alias
}, details.plugin));
});
break;
case 'oneMorphToOne':
case 'oneMorphToMany':
break;
default:
}
}
return acc;
}, {});
if (!_.isEmpty(values)) {
virtualFields.push(Model
.forge({
[Model.primaryKey]: params[Model.primaryKey]
})
.save(values, {
patch: true
}));
} else {
virtualFields.push(Promise.resolve(_.assign(response, params.values)));
}
// Update virtuals fields.
await Promise.all(virtualFields);
},
addRelation: async function (model, params, source) {
const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
_.assign(acc, strapi.plugins[current].models);
return acc;
}, {}));
const Model = models[model];
const association = Model.associations.filter(x => x.via === params.foreignKey)[0];
if (!association) {
// Resolve silently.
return Promise.resolve();
}
switch (association.nature) {
case 'oneToOne':
case 'oneToMany':
return this.manageRelations(model, params);
case 'manyToMany':
return Model.forge({
[Model.primaryKey]: parseFloat(params[Model.primaryKey])
})[association.alias]().attach(params.values[Model.primaryKey]);
default:
// Resolve silently.
return Promise.resolve();
}
},
removeRelation: async function (model, params, source) {
const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
_.assign(acc, strapi.plugins[current].models);
return acc;
}, {}));
const Model = models[model];
const association = Model.associations.filter(x => x.via === params.foreignKey)[0];
if (!association) {
// Resolve silently.
return Promise.resolve();
}
switch (association.nature) {
case 'oneToOne':
case 'oneToMany':
return this.manageRelations(model, params);
case 'manyToMany':
return Model.forge({
[Model.primaryKey]: parseFloat(params[Model.primaryKey])
})[association.alias]().detach(params.values[Model.primaryKey]);
default:
// Resolve silently.
return Promise.resolve();
}
},
addRelationMorph: async function (model, params, source) {
const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
_.assign(acc, strapi.plugins[current].models);
return acc;
}, {}));
const Model = models[model];
const record = await Model.morph.forge()
.where({
[`${Model.collectionName}_id`]: params.id,
[`${params.alias}_id`]: params.refId,
[`${params.alias}_type`]: params.ref,
field: params.field
})
.fetch({
withRelated: Model.associations.map(x => x.alias)
});
const entry = record ? record.toJSON() : record;
if (entry) {
return Promise.resolve();
}
return await Model.morph.forge({
[`${Model.collectionName}_id`]: params.id,
[`${params.alias}_id`]: params.refId,
[`${params.alias}_type`]: params.ref,
field: params.field
})
.save();
},
removeRelationMorph: async function (model, params, source) {
const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
_.assign(acc, strapi.plugins[current].models);
return acc;
}, {}));
const Model = models[model];
return await Model.morph.forge()
.where({
[`${Model.collectionName}_id`]: params.id,
[`${params.alias}_id`]: params.refId,
[`${params.alias}_type`]: params.ref,
field: params.field
})
.destroy();
}
}, relations);
};
return hook;
};