mirror of
https://github.com/strapi/strapi.git
synced 2025-07-27 02:44:13 +00:00
[WIP] Polymorphic associations with Bookshelf
This commit is contained in:
parent
128a3a67e6
commit
dc75b51d6f
@ -1,16 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[{package.json,*.yml}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
@ -133,6 +133,30 @@ module.exports = function(strapi) {
|
|||||||
: key;
|
: key;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (definition.globalId === 'Upload') {
|
||||||
|
loadedModel.serialize = function(options) {
|
||||||
|
const attrs = _.clone(this.attributes);
|
||||||
|
|
||||||
|
if (options && options.shallow) {
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relations = this.relations;
|
||||||
|
|
||||||
|
for (let key in relations) {
|
||||||
|
const relation = relations[key];
|
||||||
|
|
||||||
|
attrs[key] = relation.toJSON ? relation.toJSON(options) : relation;
|
||||||
|
|
||||||
|
if (key === 'related') {
|
||||||
|
attrs[key] = attrs[key].map(rel => rel['related']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize lifecycle callbacks.
|
// Initialize lifecycle callbacks.
|
||||||
loadedModel.initialize = function() {
|
loadedModel.initialize = function() {
|
||||||
const lifecycle = {
|
const lifecycle = {
|
||||||
@ -150,6 +174,22 @@ module.exports = function(strapi) {
|
|||||||
saved: 'afterSave'
|
saved: 'afterSave'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (definition.globalId === 'Upload') {
|
||||||
|
this.on('fetching fetching:collection', (instance, attrs, options) => {
|
||||||
|
if (_.isArray(options.withRelated)) {
|
||||||
|
options.withRelated = options.withRelated.map(path => {
|
||||||
|
if (_.isString(path) && path === 'related') {
|
||||||
|
return 'related.related';
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_.forEach(lifecycle, (fn, key) => {
|
_.forEach(lifecycle, (fn, key) => {
|
||||||
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
||||||
this.on(key, target[model.toLowerCase()][fn]);
|
this.on(key, target[model.toLowerCase()][fn]);
|
||||||
@ -369,6 +409,69 @@ module.exports = function(strapi) {
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'morphOne': {
|
||||||
|
loadedModel[name] = function() {
|
||||||
|
return this.morphOne(GLOBALS[globalId], details.via);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'morphMany': {
|
||||||
|
loadedModel[name] = function() {
|
||||||
|
return this.morphMany(GLOBALS[globalId], details.via);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'belongsToMorph':
|
||||||
|
case 'belongsToManyMorph': {
|
||||||
|
const association = definition.associations
|
||||||
|
.find(association => association.alias === name);
|
||||||
|
|
||||||
|
// console.log("coucou");
|
||||||
|
// console.log(association.related.map(id => GLOBALS[id]));
|
||||||
|
|
||||||
|
const morphValues = association.related.map(id => {
|
||||||
|
let models = Object.values(strapi.models).filter(model => model.globalId === id);
|
||||||
|
|
||||||
|
if (models.length === 0) {
|
||||||
|
models = Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||||
|
const models = Object.values(strapi.plugins[current].models).filter(model => model.globalId === id);
|
||||||
|
|
||||||
|
if (acc.length === 0 && models.length > 0) {
|
||||||
|
acc = models;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (models.length === 0) {
|
||||||
|
strapi.log.error('Impossible to register the `' + model + '` model.');
|
||||||
|
strapi.log.error('The collection name cannot be found for the morphTo method.');
|
||||||
|
strapi.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return models[0].collectionName;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Define new model.
|
||||||
|
const options = {
|
||||||
|
tableName: `${loadedModel.tableName}_morph`,
|
||||||
|
related: function () {
|
||||||
|
return this.morphTo(name, ...association.related.map((id, index) => [GLOBALS[id], morphValues[index]]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MorphModel = ORM.Model.extend(options);
|
||||||
|
|
||||||
|
loadedModel[name] = function () {
|
||||||
|
return this.hasMany(
|
||||||
|
MorphModel,
|
||||||
|
'upload_id'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
},
|
},
|
||||||
"main": "./lib",
|
"main": "./lib",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bookshelf": "^0.10.3",
|
"bookshelf": "^0.12.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"pluralize": "^6.0.0",
|
"pluralize": "^6.0.0",
|
||||||
"strapi-knex": "3.0.0-alpha.9.3",
|
"strapi-knex": "3.0.0-alpha.9.3",
|
||||||
|
@ -296,20 +296,6 @@ module.exports = function (strapi) {
|
|||||||
justOne: true
|
justOne: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set this info to be able to see if this field is a real database's field.
|
|
||||||
details.isVirtual = true;
|
|
||||||
} else if (FK.nature === 'oneToMorph') {
|
|
||||||
const key = details.plugin ?
|
|
||||||
strapi.plugins[details.plugin].models[details.model].attributes[details.via].key:
|
|
||||||
strapi.models[details.model].attributes[details.via].key;
|
|
||||||
|
|
||||||
definition.loadedModel[name] = {
|
|
||||||
type: 'virtual',
|
|
||||||
ref,
|
|
||||||
via: `${FK.via}.${key}`,
|
|
||||||
justOne: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
||||||
} else {
|
} else {
|
||||||
@ -326,7 +312,7 @@ module.exports = function (strapi) {
|
|||||||
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;
|
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.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 && FK.nature !== 'manyToMorph') {
|
if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) {
|
||||||
definition.loadedModel[name] = {
|
definition.loadedModel[name] = {
|
||||||
type: 'virtual',
|
type: 'virtual',
|
||||||
ref,
|
ref,
|
||||||
@ -335,7 +321,35 @@ module.exports = function (strapi) {
|
|||||||
|
|
||||||
// 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;
|
||||||
} else if (FK.nature === 'manyToMorph') {
|
} else {
|
||||||
|
definition.loadedModel[name] = [{
|
||||||
|
type: instance.Schema.Types.ObjectId,
|
||||||
|
ref
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'morphOne': {
|
||||||
|
const FK = _.find(definition.associations, {alias: name});
|
||||||
|
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId;
|
||||||
|
const key = details.plugin ?
|
||||||
|
strapi.plugins[details.plugin].models[details.model].attributes[details.via].key:
|
||||||
|
strapi.models[details.model].attributes[details.via].key;
|
||||||
|
|
||||||
|
definition.loadedModel[name] = {
|
||||||
|
type: 'virtual',
|
||||||
|
ref,
|
||||||
|
via: `${FK.via}.${key}`,
|
||||||
|
justOne: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set this info to be able to see if this field is a real database's field.
|
||||||
|
details.isVirtual = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'morphMany': {
|
||||||
|
const FK = _.find(definition.associations, {alias: name});
|
||||||
|
const ref = details.plugin ? strapi.plugins[details.plugin].models[details.collection].globalId : strapi.models[details.collection].globalId;
|
||||||
const key = details.plugin ?
|
const key = details.plugin ?
|
||||||
strapi.plugins[details.plugin].models[details.collection].attributes[details.via].key:
|
strapi.plugins[details.plugin].models[details.collection].attributes[details.via].key:
|
||||||
strapi.models[details.collection].attributes[details.via].key;
|
strapi.models[details.collection].attributes[details.via].key;
|
||||||
@ -348,12 +362,6 @@ module.exports = function (strapi) {
|
|||||||
|
|
||||||
// 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;
|
||||||
} else {
|
|
||||||
definition.loadedModel[name] = [{
|
|
||||||
type: instance.Schema.Types.ObjectId,
|
|
||||||
ref
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'belongsToMorph': {
|
case 'belongsToMorph': {
|
||||||
|
@ -38,6 +38,14 @@
|
|||||||
"via": "users",
|
"via": "users",
|
||||||
"plugin": "users-permissions",
|
"plugin": "users-permissions",
|
||||||
"configurable": false
|
"configurable": false
|
||||||
|
},
|
||||||
|
"avatar": {
|
||||||
|
"model": "upload",
|
||||||
|
"via": "related"
|
||||||
|
},
|
||||||
|
"photos": {
|
||||||
|
"collection": "upload",
|
||||||
|
"via": "related"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,12 +199,12 @@ module.exports = {
|
|||||||
if (types.current === 'collection' && types.other === 'morphTo') {
|
if (types.current === 'collection' && types.other === 'morphTo') {
|
||||||
return {
|
return {
|
||||||
nature: 'manyToMorph',
|
nature: 'manyToMorph',
|
||||||
verbose: 'belongsToMany'
|
verbose: 'morphMany'
|
||||||
};
|
};
|
||||||
} else if (types.current === 'modelD' && types.other === 'morphTo') {
|
} else if (types.current === 'modelD' && types.other === 'morphTo') {
|
||||||
return {
|
return {
|
||||||
nature: 'oneToMorph',
|
nature: 'oneToMorph',
|
||||||
verbose: 'belongsTo'
|
verbose: 'morphOne'
|
||||||
};
|
};
|
||||||
} else if (types.current === 'morphTo' && types.other === 'collection') {
|
} else if (types.current === 'morphTo' && types.other === 'collection') {
|
||||||
return {
|
return {
|
||||||
@ -324,9 +324,43 @@ module.exports = {
|
|||||||
where: details.where,
|
where: details.where,
|
||||||
});
|
});
|
||||||
} else if (association.hasOwnProperty('key')) {
|
} else if (association.hasOwnProperty('key')) {
|
||||||
|
const pluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => {
|
||||||
|
Object.keys(strapi.plugins[current].models).forEach((entity) => {
|
||||||
|
Object.keys(strapi.plugins[current].models[entity].attributes).forEach((attribute) => {
|
||||||
|
const attr = strapi.plugins[current].models[entity].attributes[attribute];
|
||||||
|
if (
|
||||||
|
(attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
|
||||||
|
strapi.plugins[current].models[entity].globalId !== definition.globalId
|
||||||
|
) {
|
||||||
|
acc.push(strapi.plugins[current].models[entity].globalId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const appModels = Object.keys(strapi.models).reduce((acc, entity) => {
|
||||||
|
Object.keys(strapi.models[entity].attributes).forEach((attribute) => {
|
||||||
|
const attr = strapi.models[entity].attributes[attribute];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() &&
|
||||||
|
strapi.models[entity].globalId !== definition.globalId
|
||||||
|
) {
|
||||||
|
acc.push(strapi.models[entity].globalId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const models = _.uniq(appModels.concat(pluginsModels));
|
||||||
|
|
||||||
definition.associations.push({
|
definition.associations.push({
|
||||||
alias: key,
|
alias: key,
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
|
related: models,
|
||||||
nature: infos.nature,
|
nature: infos.nature,
|
||||||
autoPopulate: _.get(association, 'autoPopulate', true),
|
autoPopulate: _.get(association, 'autoPopulate', true),
|
||||||
key: association.key,
|
key: association.key,
|
||||||
|
@ -154,8 +154,8 @@ module.exports = {
|
|||||||
|
|
||||||
CREATE TABLE ${quote}${Model.tableName || Model.collectionName}${quote} (
|
CREATE TABLE ${quote}${Model.tableName || Model.collectionName}${quote} (
|
||||||
id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
|
id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
|
||||||
key text,
|
${quote}key${quote} text,
|
||||||
value text,
|
${quote}value${quote} text,
|
||||||
environment text,
|
environment text,
|
||||||
type text,
|
type text,
|
||||||
tag text
|
tag text
|
||||||
|
@ -53,6 +53,9 @@ module.exports = strapi => {
|
|||||||
|
|
||||||
// Recursive to mask the private properties.
|
// Recursive to mask the private properties.
|
||||||
const mask = (payload) => {
|
const mask = (payload) => {
|
||||||
|
// Handle ORM toJSON() method to work on real JSON object.
|
||||||
|
payload = payload.toJSON ? payload.toJSON() : payload;
|
||||||
|
|
||||||
if (_.isArray(payload)) {
|
if (_.isArray(payload)) {
|
||||||
return payload.map(mask);
|
return payload.map(mask);
|
||||||
} else if (_.isPlainObject(payload)) {
|
} else if (_.isPlainObject(payload)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user