mirror of
https://github.com/strapi/strapi.git
synced 2025-07-26 02:20:32 +00:00
Group schema + auto populate for mongoose
This commit is contained in:
parent
2753176d89
commit
5f29e81556
@ -119,7 +119,8 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
|
|
||||||
// Add every relationships to the loaded model for Bookshelf.
|
// Add every relationships to the loaded model for Bookshelf.
|
||||||
// Basic attributes don't need this-- only relations.
|
// Basic attributes don't need this-- only relations.
|
||||||
_.forEach(definition.attributes, (details, name) => {
|
Object.keys(definition.attributes).forEach(name => {
|
||||||
|
const details = definition.attributes[name];
|
||||||
if (details.type !== undefined) {
|
if (details.type !== undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -433,7 +434,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
attrs[key] =
|
attrs[key] =
|
||||||
definition.attributes[key].repeatable === true
|
definition.attributes[key].repeatable === true
|
||||||
? groups
|
? groups
|
||||||
: _.first(groups);
|
: _.first(groups) || null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ module.exports = function(strapi) {
|
|||||||
.map(async connectionName => {
|
.map(async connectionName => {
|
||||||
const connection = connections[connectionName];
|
const connection = connections[connectionName];
|
||||||
const instance = new Mongoose();
|
const instance = new Mongoose();
|
||||||
|
|
||||||
_.defaults(connection.settings, strapi.config.hook.settings.mongoose);
|
_.defaults(connection.settings, strapi.config.hook.settings.mongoose);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -65,9 +66,6 @@ module.exports = function(strapi) {
|
|||||||
|
|
||||||
// Connect to mongo database
|
// Connect to mongo database
|
||||||
const connectOptions = {};
|
const connectOptions = {};
|
||||||
const options = {
|
|
||||||
useFindAndModify: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!_.isEmpty(username)) {
|
if (!_.isEmpty(username)) {
|
||||||
connectOptions.user = username;
|
connectOptions.user = username;
|
||||||
@ -86,8 +84,6 @@ module.exports = function(strapi) {
|
|||||||
connectOptions.dbName = database;
|
connectOptions.dbName = database;
|
||||||
connectOptions.useCreateIndex = true;
|
connectOptions.useCreateIndex = true;
|
||||||
|
|
||||||
options.debug = debug === true || debug === 'true';
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* FIXME: for now, mongoose doesn't support srv auth except the way including user/pass in URI.
|
/* FIXME: for now, mongoose doesn't support srv auth except the way including user/pass in URI.
|
||||||
* https://github.com/Automattic/mongoose/issues/6881 */
|
* https://github.com/Automattic/mongoose/issues/6881 */
|
||||||
@ -117,7 +113,8 @@ module.exports = function(strapi) {
|
|||||||
require(initFunctionPath)(instance, connection);
|
require(initFunctionPath)(instance, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(options, key => instance.set(key, options[key]));
|
instance.set('debug', debug === true || debug === 'true');
|
||||||
|
instance.set('useFindAndModify', false);
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
instance,
|
instance,
|
||||||
|
@ -2,41 +2,107 @@
|
|||||||
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
const mongooseUtils = require('mongoose/lib/utils');
|
|
||||||
|
|
||||||
const utilsModels = require('strapi-utils').models;
|
const utilsModels = require('strapi-utils').models;
|
||||||
const utils = require('./utils/');
|
const utils = require('./utils');
|
||||||
const relations = require('./relations');
|
const relations = require('./relations');
|
||||||
|
|
||||||
module.exports = ({ models, target, plugin = false }, ctx) => {
|
module.exports = ({ models, target, plugin = false }, ctx) => {
|
||||||
const { instance } = ctx;
|
const { instance } = ctx;
|
||||||
|
|
||||||
const loadedAttributes = _.after(_.size(models), () => {
|
// Parse every authenticated model.
|
||||||
_.forEach(models, (definition, model) => {
|
Object.keys(models).map(model => {
|
||||||
try {
|
const definition = models[model];
|
||||||
let collection =
|
definition.orm = 'mongoose';
|
||||||
strapi.config.hook.settings.mongoose.collections[
|
definition.associations = [];
|
||||||
mongooseUtils.toCollectionName(definition.globalName)
|
definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
|
||||||
];
|
definition.loadedModel = {};
|
||||||
|
|
||||||
// Set the default values to model settings.
|
// Set the default values to model settings.
|
||||||
_.defaults(definition, {
|
_.defaults(definition, {
|
||||||
primaryKey: '_id',
|
primaryKey: '_id',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!plugin) {
|
||||||
|
global[definition.globalName] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupAttributes = Object.keys(definition.attributes).filter(
|
||||||
|
key => definition.attributes[key].type === 'group'
|
||||||
|
);
|
||||||
|
|
||||||
|
const scalarAttributes = Object.keys(definition.attributes).filter(key => {
|
||||||
|
const { type } = definition.attributes[key];
|
||||||
|
return type !== undefined && type !== null && type !== 'group';
|
||||||
|
});
|
||||||
|
|
||||||
|
const relationalAttributes = Object.keys(definition.attributes).filter(
|
||||||
|
key => {
|
||||||
|
const { type } = definition.attributes[key];
|
||||||
|
return type === undefined;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// handle gorup attrs
|
||||||
|
if (groupAttributes.length > 0) {
|
||||||
|
// create join morph collection thingy
|
||||||
|
groupAttributes.forEach(name => {
|
||||||
|
definition.loadedModel[name] = [
|
||||||
|
{
|
||||||
|
kind: String,
|
||||||
|
ref: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
refPath: `${name}.kind`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle scalar attrs
|
||||||
|
scalarAttributes.forEach(name => {
|
||||||
|
const attr = definition.attributes[name];
|
||||||
|
|
||||||
|
definition.loadedModel[name] = {
|
||||||
|
...attr,
|
||||||
|
type: utils(instance).convertType(attr.type),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle relational attrs
|
||||||
|
relationalAttributes.forEach(name => {
|
||||||
|
buildRelation({
|
||||||
|
definition,
|
||||||
|
model,
|
||||||
|
instance,
|
||||||
|
name,
|
||||||
|
attribute: definition.attributes[name],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const schema = new instance.Schema(
|
||||||
|
_.omitBy(definition.loadedModel, ({ type }) => type === 'virtual')
|
||||||
|
);
|
||||||
|
|
||||||
// Initialize lifecycle callbacks.
|
// Initialize lifecycle callbacks.
|
||||||
const preLifecycle = {
|
const preLifecycle = {
|
||||||
validate: 'beforeCreate',
|
validate: 'beforeCreate',
|
||||||
|
find: 'beforeFetchAll',
|
||||||
|
findOne: 'beforeFetch',
|
||||||
findOneAndUpdate: 'beforeUpdate',
|
findOneAndUpdate: 'beforeUpdate',
|
||||||
findOneAndRemove: 'beforeDestroy',
|
findOneAndRemove: 'beforeDestroy',
|
||||||
remove: 'beforeDestroy',
|
remove: 'beforeDestroy',
|
||||||
update: 'beforeUpdate',
|
update: 'beforeUpdate',
|
||||||
updateOne: 'beforeUpdate',
|
updateOne: 'beforeUpdate',
|
||||||
find: 'beforeFetchAll',
|
|
||||||
findOne: 'beforeFetch',
|
|
||||||
save: 'beforeSave',
|
save: 'beforeSave',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findLifecycles = [
|
||||||
|
'find',
|
||||||
|
'findOne',
|
||||||
|
'findOneAndUpdate',
|
||||||
|
'findOneAndRemove',
|
||||||
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Override populate path for polymorphic association.
|
Override populate path for polymorphic association.
|
||||||
It allows us to make Upload.find().populate('related')
|
It allows us to make Upload.find().populate('related')
|
||||||
@ -44,77 +110,24 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const morphAssociations = definition.associations.filter(
|
const morphAssociations = definition.associations.filter(
|
||||||
association =>
|
association => association.nature.toLowerCase().indexOf('morph') !== -1
|
||||||
association.nature.toLowerCase().indexOf('morph') !== -1
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (morphAssociations.length > 0) {
|
const populateFn = createOnFetchPopulateFn({
|
||||||
morphAssociations.forEach(association => {
|
groupAttributes,
|
||||||
Object.keys(preLifecycle)
|
morphAssociations,
|
||||||
.filter(key => key.indexOf('find') !== -1)
|
definition,
|
||||||
.forEach(key => {
|
|
||||||
collection.schema.pre(key, function(next) {
|
|
||||||
if (
|
|
||||||
this._mongooseOptions.populate &&
|
|
||||||
this._mongooseOptions.populate[association.alias]
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
association.nature === 'oneToManyMorph' ||
|
|
||||||
association.nature === 'manyToManyMorph'
|
|
||||||
) {
|
|
||||||
this._mongooseOptions.populate[
|
|
||||||
association.alias
|
|
||||||
].match = {
|
|
||||||
[`${association.via}.${association.filter}`]: association.alias,
|
|
||||||
[`${association.via}.kind`]: definition.globalId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Select last related to an entity.
|
|
||||||
this._mongooseOptions.populate[
|
|
||||||
association.alias
|
|
||||||
].options = {
|
|
||||||
sort: '-createdAt',
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this._mongooseOptions.populate[
|
|
||||||
association.alias
|
|
||||||
].path = `${association.alias}.ref`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!this._mongooseOptions.populate) {
|
|
||||||
this._mongooseOptions.populate = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Images are not displayed in populated data.
|
|
||||||
// We automatically populate morph relations.
|
|
||||||
if (
|
|
||||||
association.nature === 'oneToManyMorph' ||
|
|
||||||
association.nature === 'manyToManyMorph'
|
|
||||||
) {
|
|
||||||
this._mongooseOptions.populate[association.alias] = {
|
|
||||||
path: association.alias,
|
|
||||||
match: {
|
|
||||||
[`${association.via}.${association.filter}`]: association.alias,
|
|
||||||
[`${association.via}.kind`]: definition.globalId,
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
sort: '-createdAt',
|
|
||||||
},
|
|
||||||
select: undefined,
|
|
||||||
model: undefined,
|
|
||||||
_docs: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_.forEach(preLifecycle, (fn, key) => {
|
findLifecycles.forEach(key => {
|
||||||
|
schema.pre(key, populateFn);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(preLifecycle).forEach(key => {
|
||||||
|
const fn = preLifecycle[key];
|
||||||
|
|
||||||
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
||||||
collection.schema.pre(key, function(next) {
|
schema.pre(key, function(next) {
|
||||||
target[model.toLowerCase()]
|
target[model.toLowerCase()]
|
||||||
[fn](this)
|
[fn](this)
|
||||||
.then(next)
|
.then(next)
|
||||||
@ -136,9 +149,11 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
|
|
||||||
// Mongoose doesn't allow post 'remove' event on model.
|
// Mongoose doesn't allow post 'remove' event on model.
|
||||||
// See https://github.com/Automattic/mongoose/issues/3054
|
// See https://github.com/Automattic/mongoose/issues/3054
|
||||||
_.forEach(postLifecycle, (fn, key) => {
|
Object.keys(postLifecycle).forEach(key => {
|
||||||
|
const fn = postLifecycle[key];
|
||||||
|
|
||||||
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
||||||
collection.schema.post(key, function(doc, next) {
|
schema.post(key, function(doc, next) {
|
||||||
target[model.toLowerCase()]
|
target[model.toLowerCase()]
|
||||||
[fn](this, doc)
|
[fn](this, doc)
|
||||||
.then(next)
|
.then(next)
|
||||||
@ -156,7 +171,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
return model.type === 'virtual';
|
return model.type === 'virtual';
|
||||||
}),
|
}),
|
||||||
(value, key) => {
|
(value, key) => {
|
||||||
collection.schema.virtual(key.replace('_v', ''), {
|
schema.virtual(key.replace('_v', ''), {
|
||||||
ref: value.ref,
|
ref: value.ref,
|
||||||
localField: '_id',
|
localField: '_id',
|
||||||
foreignField: value.via,
|
foreignField: value.via,
|
||||||
@ -175,9 +190,9 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
? _.get(definition, 'options.timestamps[1]')
|
? _.get(definition, 'options.timestamps[1]')
|
||||||
: 'updatedAt',
|
: 'updatedAt',
|
||||||
};
|
};
|
||||||
collection.schema.set('timestamps', timestamps);
|
schema.set('timestamps', timestamps);
|
||||||
} else {
|
} else {
|
||||||
collection.schema.set(
|
schema.set(
|
||||||
'timestamps',
|
'timestamps',
|
||||||
_.get(definition, 'options.timestamps') === true
|
_.get(definition, 'options.timestamps') === true
|
||||||
);
|
);
|
||||||
@ -189,7 +204,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
: false
|
: false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
collection.schema.set(
|
schema.set(
|
||||||
'minimize',
|
'minimize',
|
||||||
_.get(definition, 'options.minimize', false) === true
|
_.get(definition, 'options.minimize', false) === true
|
||||||
);
|
);
|
||||||
@ -197,7 +212,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
// Save all attributes (with timestamps)
|
// Save all attributes (with timestamps)
|
||||||
target[model].allAttributes = _.clone(definition.attributes);
|
target[model].allAttributes = _.clone(definition.attributes);
|
||||||
|
|
||||||
collection.schema.options.toObject = collection.schema.options.toJSON = {
|
schema.options.toObject = schema.options.toJSON = {
|
||||||
virtuals: true,
|
virtuals: true,
|
||||||
transform: function(doc, returned) {
|
transform: function(doc, returned) {
|
||||||
// Remover $numberDecimal nested property.
|
// Remover $numberDecimal nested property.
|
||||||
@ -221,21 +236,32 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
break;
|
break;
|
||||||
case 'manyMorphToMany':
|
case 'manyMorphToMany':
|
||||||
case 'manyMorphToOne':
|
case 'manyMorphToOne':
|
||||||
returned[association.alias] = returned[
|
returned[association.alias] = returned[association.alias].map(
|
||||||
association.alias
|
obj => obj.ref
|
||||||
].map(obj => obj.ref);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
groupAttributes.forEach(name => {
|
||||||
|
const attribute = definition.attributes[name];
|
||||||
|
|
||||||
|
if (Array.isArray(returned[name])) {
|
||||||
|
const groups = returned[name].map(el => el.ref);
|
||||||
|
// Reformat data by bypassing the many-to-many relationship.
|
||||||
|
returned[name] =
|
||||||
|
attribute.repeatable === true ? groups : _.first(groups) || null;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Instantiate model.
|
// Instantiate model.
|
||||||
const Model = instance.model(
|
const Model = instance.model(
|
||||||
definition.globalId,
|
definition.globalId,
|
||||||
collection.schema,
|
schema,
|
||||||
definition.collectionName
|
definition.collectionName
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -249,94 +275,87 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
// Push attributes to be aware of model schema.
|
// Push attributes to be aware of model schema.
|
||||||
target[model]._attributes = definition.attributes;
|
target[model]._attributes = definition.attributes;
|
||||||
target[model].updateRelations = relations.update;
|
target[model].updateRelations = relations.update;
|
||||||
} catch (err) {
|
});
|
||||||
strapi.log.error('Impossible to register the `' + model + '` model.');
|
};
|
||||||
strapi.log.error(err);
|
|
||||||
strapi.stop();
|
const createOnFetchPopulateFn = ({
|
||||||
|
morphAssociations,
|
||||||
|
groupAttributes,
|
||||||
|
definition,
|
||||||
|
}) => {
|
||||||
|
return function(next) {
|
||||||
|
morphAssociations.forEach(association => {
|
||||||
|
if (
|
||||||
|
this._mongooseOptions.populate &&
|
||||||
|
this._mongooseOptions.populate[association.alias]
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
association.nature === 'oneToManyMorph' ||
|
||||||
|
association.nature === 'manyToManyMorph'
|
||||||
|
) {
|
||||||
|
this._mongooseOptions.populate[association.alias].match = {
|
||||||
|
[`${association.via}.${association.filter}`]: association.alias,
|
||||||
|
[`${association.via}.kind`]: definition.globalId,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select last related to an entity.
|
||||||
|
this._mongooseOptions.populate[association.alias].options = {
|
||||||
|
sort: '-createdAt',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._mongooseOptions.populate[
|
||||||
|
association.alias
|
||||||
|
].path = `${association.alias}.ref`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this._mongooseOptions.populate) {
|
||||||
|
this._mongooseOptions.populate = {};
|
||||||
|
}
|
||||||
|
// Images are not displayed in populated data.
|
||||||
|
// We automatically populate morph relations.
|
||||||
|
if (
|
||||||
|
association.nature === 'oneToManyMorph' ||
|
||||||
|
association.nature === 'manyToManyMorph'
|
||||||
|
) {
|
||||||
|
this._mongooseOptions.populate[association.alias] = {
|
||||||
|
path: association.alias,
|
||||||
|
match: {
|
||||||
|
[`${association.via}.${association.filter}`]: association.alias,
|
||||||
|
[`${association.via}.kind`]: definition.globalId,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
sort: '-createdAt',
|
||||||
|
},
|
||||||
|
select: undefined,
|
||||||
|
model: undefined,
|
||||||
|
_docs: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
groupAttributes.forEach(name => {
|
||||||
|
if (
|
||||||
|
this._mongooseOptions.populate &&
|
||||||
|
this._mongooseOptions.populate[name]
|
||||||
|
) {
|
||||||
|
this._mongooseOptions.populate[name].path = `${name}.ref`;
|
||||||
|
} else {
|
||||||
|
this._mongooseOptions.populate[name] = {
|
||||||
|
path: `${name}.ref`,
|
||||||
|
_docs: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Parse every authenticated model.
|
next();
|
||||||
_.forEach(models, (definition, model) => {
|
};
|
||||||
definition.globalName = _.upperFirst(_.camelCase(definition.globalId));
|
};
|
||||||
|
|
||||||
// Make sure the model has a connection.
|
const buildRelation = ({ definition, model, instance, attribute, name }) => {
|
||||||
// If not, use the default connection.
|
|
||||||
if (_.isEmpty(definition.connection)) {
|
|
||||||
definition.connection =
|
|
||||||
strapi.config.currentEnvironment.database.defaultConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure this connection exists.
|
|
||||||
if (!_.has(strapi.config.connections, definition.connection)) {
|
|
||||||
strapi.log.error(
|
|
||||||
'The connection `' +
|
|
||||||
definition.connection +
|
|
||||||
'` specified in the `' +
|
|
||||||
model +
|
|
||||||
'` model does not exist.'
|
|
||||||
);
|
|
||||||
strapi.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some informations about ORM & client connection
|
|
||||||
definition.orm = 'mongoose';
|
|
||||||
definition.client = _.get(
|
|
||||||
strapi.config.connections[definition.connection],
|
|
||||||
'client'
|
|
||||||
);
|
|
||||||
definition.associations = [];
|
|
||||||
|
|
||||||
// Register the final model for Mongoose.
|
|
||||||
definition.loadedModel = _.cloneDeep(definition.attributes);
|
|
||||||
|
|
||||||
// Initialize the global variable with the
|
|
||||||
// capitalized model name.
|
|
||||||
if (!plugin) {
|
|
||||||
global[definition.globalName] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.isEmpty(definition.attributes)) {
|
|
||||||
// Generate empty schema
|
|
||||||
_.set(
|
|
||||||
strapi.config.hook.settings.mongoose,
|
|
||||||
'collections.' +
|
|
||||||
mongooseUtils.toCollectionName(definition.globalName) +
|
|
||||||
'.schema',
|
|
||||||
new instance.Schema({})
|
|
||||||
);
|
|
||||||
|
|
||||||
return loadedAttributes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call this callback function after we are done parsing
|
|
||||||
// all attributes for relationships-- see below.
|
|
||||||
const done = _.after(_.size(definition.attributes), () => {
|
|
||||||
// Generate schema without virtual populate
|
|
||||||
const schema = new instance.Schema(
|
|
||||||
_.omitBy(definition.loadedModel, model => {
|
|
||||||
return model.type === 'virtual';
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
_.set(
|
|
||||||
strapi.config.hook.settings.mongoose,
|
|
||||||
'collections.' +
|
|
||||||
mongooseUtils.toCollectionName(definition.globalName) +
|
|
||||||
'.schema',
|
|
||||||
schema
|
|
||||||
);
|
|
||||||
|
|
||||||
loadedAttributes();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add every relationships to the loaded model for Bookshelf.
|
|
||||||
// Basic attributes don't need this-- only relations.
|
|
||||||
_.forEach(definition.attributes, (details, name) => {
|
|
||||||
const verbose =
|
const verbose =
|
||||||
_.get(
|
_.get(
|
||||||
utilsModels.getNature(details, name, undefined, model.toLowerCase()),
|
utilsModels.getNature(attribute, name, undefined, model.toLowerCase()),
|
||||||
'verbose'
|
'verbose'
|
||||||
) || '';
|
) || '';
|
||||||
|
|
||||||
@ -344,21 +363,15 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
utilsModels.defineAssociations(
|
utilsModels.defineAssociations(
|
||||||
model.toLowerCase(),
|
model.toLowerCase(),
|
||||||
definition,
|
definition,
|
||||||
details,
|
attribute,
|
||||||
name
|
name
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_.isEmpty(verbose)) {
|
|
||||||
definition.loadedModel[name].type = utils(instance).convertType(
|
|
||||||
details.type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (verbose) {
|
switch (verbose) {
|
||||||
case 'hasOne': {
|
case 'hasOne': {
|
||||||
const ref = details.plugin
|
const ref = attribute.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.model].globalId
|
? strapi.plugins[attribute.plugin].models[attribute.model].globalId
|
||||||
: strapi.models[details.model].globalId;
|
: strapi.models[attribute.model].globalId;
|
||||||
|
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
type: instance.Schema.Types.ObjectId,
|
type: instance.Schema.Types.ObjectId,
|
||||||
@ -370,9 +383,9 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
const FK = _.find(definition.associations, {
|
const FK = _.find(definition.associations, {
|
||||||
alias: name,
|
alias: name,
|
||||||
});
|
});
|
||||||
const ref = details.plugin
|
const ref = attribute.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.collection].globalId
|
? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
|
||||||
: strapi.models[details.collection].globalId;
|
: strapi.models[attribute.collection].globalId;
|
||||||
|
|
||||||
if (FK) {
|
if (FK) {
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
@ -383,7 +396,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
attribute.isVirtual = true;
|
||||||
} else {
|
} else {
|
||||||
definition.loadedModel[name] = [
|
definition.loadedModel[name] = [
|
||||||
{
|
{
|
||||||
@ -398,9 +411,9 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
const FK = _.find(definition.associations, {
|
const FK = _.find(definition.associations, {
|
||||||
alias: name,
|
alias: name,
|
||||||
});
|
});
|
||||||
const ref = details.plugin
|
const ref = attribute.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.model].globalId
|
? strapi.plugins[attribute.plugin].models[attribute.model].globalId
|
||||||
: strapi.models[details.model].globalId;
|
: strapi.models[attribute.model].globalId;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
FK &&
|
FK &&
|
||||||
@ -417,7 +430,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
attribute.isVirtual = true;
|
||||||
} else {
|
} else {
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
type: instance.Schema.Types.ObjectId,
|
type: instance.Schema.Types.ObjectId,
|
||||||
@ -431,12 +444,12 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
const FK = _.find(definition.associations, {
|
const FK = _.find(definition.associations, {
|
||||||
alias: name,
|
alias: name,
|
||||||
});
|
});
|
||||||
const ref = details.plugin
|
const ref = attribute.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.collection].globalId
|
? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
|
||||||
: strapi.models[details.collection].globalId;
|
: strapi.models[attribute.collection].globalId;
|
||||||
|
|
||||||
// One-side of the relationship has to be a virtual field to be bidirectional.
|
// One-side of the relationship has to be a virtual field to be bidirectional.
|
||||||
if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) {
|
if ((FK && _.isUndefined(FK.via)) || attribute.dominant !== true) {
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
type: 'virtual',
|
type: 'virtual',
|
||||||
ref,
|
ref,
|
||||||
@ -444,7 +457,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
attribute.isVirtual = true;
|
||||||
} else {
|
} else {
|
||||||
definition.loadedModel[name] = [
|
definition.loadedModel[name] = [
|
||||||
{
|
{
|
||||||
@ -459,9 +472,9 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
const FK = _.find(definition.associations, {
|
const FK = _.find(definition.associations, {
|
||||||
alias: name,
|
alias: name,
|
||||||
});
|
});
|
||||||
const ref = details.plugin
|
const ref = attribute.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.model].globalId
|
? strapi.plugins[attribute.plugin].models[attribute.model].globalId
|
||||||
: strapi.models[details.model].globalId;
|
: strapi.models[attribute.model].globalId;
|
||||||
|
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
type: 'virtual',
|
type: 'virtual',
|
||||||
@ -471,16 +484,16 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
attribute.isVirtual = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'morphMany': {
|
case 'morphMany': {
|
||||||
const FK = _.find(definition.associations, {
|
const FK = _.find(definition.associations, {
|
||||||
alias: name,
|
alias: name,
|
||||||
});
|
});
|
||||||
const ref = details.plugin
|
const ref = attribute.plugin
|
||||||
? strapi.plugins[details.plugin].models[details.collection].globalId
|
? strapi.plugins[attribute.plugin].models[attribute.collection].globalId
|
||||||
: strapi.models[details.collection].globalId;
|
: strapi.models[attribute.collection].globalId;
|
||||||
|
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
type: 'virtual',
|
type: 'virtual',
|
||||||
@ -489,13 +502,13 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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;
|
attribute.isVirtual = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'belongsToMorph': {
|
case 'belongsToMorph': {
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
kind: String,
|
kind: String,
|
||||||
[details.filter]: String,
|
[attribute.filter]: String,
|
||||||
ref: {
|
ref: {
|
||||||
type: instance.Schema.Types.ObjectId,
|
type: instance.Schema.Types.ObjectId,
|
||||||
refPath: `${name}.kind`,
|
refPath: `${name}.kind`,
|
||||||
@ -507,7 +520,7 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
definition.loadedModel[name] = [
|
definition.loadedModel[name] = [
|
||||||
{
|
{
|
||||||
kind: String,
|
kind: String,
|
||||||
[details.filter]: String,
|
[attribute.filter]: String,
|
||||||
ref: {
|
ref: {
|
||||||
type: instance.Schema.Types.ObjectId,
|
type: instance.Schema.Types.ObjectId,
|
||||||
refPath: `${name}.kind`,
|
refPath: `${name}.kind`,
|
||||||
@ -519,8 +532,4 @@ module.exports = ({ models, target, plugin = false }, ctx) => {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -8,8 +8,14 @@ const Mongoose = require('mongoose');
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = (mongoose = Mongoose) => {
|
module.exports = (mongoose = Mongoose) => {
|
||||||
mongoose.Schema.Types.Decimal = require('mongoose-float').loadType(mongoose, 2);
|
mongoose.Schema.Types.Decimal = require('mongoose-float').loadType(
|
||||||
mongoose.Schema.Types.Float = require('mongoose-float').loadType(mongoose, 20);
|
mongoose,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
mongoose.Schema.Types.Float = require('mongoose-float').loadType(
|
||||||
|
mongoose,
|
||||||
|
20
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert MongoDB ID to the stringify version as GraphQL throws an error if not.
|
* Convert MongoDB ID to the stringify version as GraphQL throws an error if not.
|
||||||
@ -20,8 +26,7 @@ module.exports = (mongoose = Mongoose) => {
|
|||||||
return this.toString();
|
return this.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const utils = {
|
const convertType = mongooseType => {
|
||||||
convertType: mongooseType => {
|
|
||||||
switch (mongooseType.toLowerCase()) {
|
switch (mongooseType.toLowerCase()) {
|
||||||
case 'array':
|
case 'array':
|
||||||
return Array;
|
return Array;
|
||||||
@ -52,16 +57,11 @@ module.exports = (mongoose = Mongoose) => {
|
|||||||
case 'text':
|
case 'text':
|
||||||
return 'String';
|
return 'String';
|
||||||
default:
|
default:
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
valueToId: value => {
|
|
||||||
if (utils.isMongoId(value)) {
|
|
||||||
return mongoose.Types.ObjectId(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
const isMongoId = value => {
|
||||||
},
|
|
||||||
isMongoId: value => {
|
|
||||||
if (value instanceof mongoose.Types.ObjectId) {
|
if (value instanceof mongoose.Types.ObjectId) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -74,8 +74,19 @@ module.exports = (mongoose = Mongoose) => {
|
|||||||
// it returns for instance true for any integer value
|
// it returns for instance true for any integer value
|
||||||
const hexadecimal = /^[0-9A-F]+$/i;
|
const hexadecimal = /^[0-9A-F]+$/i;
|
||||||
return hexadecimal.test(value) && value.length === 24;
|
return hexadecimal.test(value) && value.length === 24;
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return utils;
|
const valueToId = value => {
|
||||||
|
if (isMongoId(value)) {
|
||||||
|
return mongoose.Types.ObjectId(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
convertType,
|
||||||
|
valueToId,
|
||||||
|
isMongoId,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user