mirror of
https://github.com/strapi/strapi.git
synced 2026-01-03 18:53:34 +00:00
Handle most polymorphic use cases with Bookshelf
This commit is contained in:
parent
dc75b51d6f
commit
dd354caf60
@ -133,28 +133,44 @@ module.exports = function(strapi) {
|
||||
: 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']);
|
||||
}
|
||||
}
|
||||
// Update serialize to reformat data for polymorphic associations.
|
||||
loadedModel.serialize = function(options) {
|
||||
const attrs = _.clone(this.attributes);
|
||||
|
||||
if (options && options.shallow) {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
const relations = this.relations;
|
||||
|
||||
definition.associations
|
||||
.filter(association => association.nature.toLowerCase().indexOf('morph') !== -1)
|
||||
.map(association => {
|
||||
// Retrieve relation Bookshelf object.
|
||||
const relation = relations[association.alias];
|
||||
|
||||
if (relation) {
|
||||
// Extract raw JSON data.
|
||||
attrs[association.alias] = relation.toJSON ? relation.toJSON(options) : relation;
|
||||
|
||||
// Retrieve opposite model.
|
||||
const model = association.plugin ?
|
||||
strapi.plugins[association.plugin].models[association.collection || association.model]:
|
||||
strapi.models[association.collection || association.model];
|
||||
|
||||
// Reformat data by bypassing the many-to-many relationship.
|
||||
if (association.nature === 'oneToMorph') {
|
||||
attrs[association.alias] = attrs[association.alias][model.collectionName];
|
||||
} else if (association.nature === 'manyToMorph') {
|
||||
attrs[association.alias] = attrs[association.alias].map(rel => rel[model.collectionName]);
|
||||
} else {
|
||||
// MorphToX cases.
|
||||
attrs[association.alias] = attrs[association.alias].map(rel => rel[association.alias]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
// Initialize lifecycle callbacks.
|
||||
@ -174,28 +190,52 @@ module.exports = function(strapi) {
|
||||
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) => {
|
||||
if (_.isFunction(target[model.toLowerCase()][fn])) {
|
||||
this.on(key, target[model.toLowerCase()][fn]);
|
||||
}
|
||||
});
|
||||
|
||||
// Update withRelated level to bypass many-to-many association for polymorphic relationshiips.
|
||||
// Apply only during fetching.
|
||||
this.on('fetching:collection', (instance, attrs, options) => {
|
||||
if (_.isArray(options.withRelated)) {
|
||||
options.withRelated = options.withRelated.map(path => {
|
||||
const association = definition.associations
|
||||
.filter(association => association.nature.toLowerCase().indexOf('morph') !== -1)
|
||||
.filter(association => association.alias === path || association.via === path)[0];
|
||||
|
||||
if (association) {
|
||||
// Override on polymorphic path only.
|
||||
if (_.isString(path) && path === association.via) {
|
||||
return `related.${association.via}`;
|
||||
} else if (_.isString(path) && path === association.alias) {
|
||||
// MorphTo side.
|
||||
if (association.related) {
|
||||
return `${association.alias}.related`;
|
||||
}
|
||||
|
||||
// oneToMorph or manyToMorph side.
|
||||
// Retrieve collection name because we are using it to build our hidden model.
|
||||
const model = association.plugin ?
|
||||
strapi.plugins[association.plugin].models[association.collection || association.model]:
|
||||
strapi.models[association.collection || association.model];
|
||||
|
||||
return `${association.alias}.${model.collectionName}`;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
});
|
||||
}
|
||||
|
||||
return _.isFunction(
|
||||
target[model.toLowerCase()]['beforeFetchCollection']
|
||||
)
|
||||
? target[model.toLowerCase()]['beforeFetchCollection']
|
||||
: Promise.resolve();
|
||||
});
|
||||
|
||||
this.on('saving', (instance, attrs, options) => {
|
||||
instance.attributes = mapper(instance.attributes);
|
||||
attrs = mapper(attrs);
|
||||
@ -410,14 +450,34 @@ module.exports = function(strapi) {
|
||||
break;
|
||||
}
|
||||
case 'morphOne': {
|
||||
const model = details.plugin ?
|
||||
strapi.plugins[details.plugin].models[details.model]:
|
||||
strapi.models[details.model];
|
||||
|
||||
const globalId = `${model.collectionName}_morph`;
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.morphOne(GLOBALS[globalId], details.via);
|
||||
return this
|
||||
.morphOne(GLOBALS[globalId], details.via, `${definition.collectionName}`)
|
||||
.query(qb => {
|
||||
qb.where('field', name);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'morphMany': {
|
||||
const collection = details.plugin ?
|
||||
strapi.plugins[details.plugin].models[details.collection]:
|
||||
strapi.models[details.collection];
|
||||
|
||||
const globalId = `${collection.collectionName}_morph`;
|
||||
|
||||
loadedModel[name] = function() {
|
||||
return this.morphMany(GLOBALS[globalId], details.via);
|
||||
return this
|
||||
.morphMany(GLOBALS[globalId], details.via, `${definition.collectionName}`)
|
||||
.query(qb => {
|
||||
qb.where('field', name);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -426,9 +486,6 @@ module.exports = function(strapi) {
|
||||
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);
|
||||
|
||||
@ -453,21 +510,37 @@ module.exports = function(strapi) {
|
||||
return models[0].collectionName;
|
||||
});
|
||||
|
||||
|
||||
// Define new model.
|
||||
const options = {
|
||||
tableName: `${loadedModel.tableName}_morph`,
|
||||
tableName: `${definition.collectionName}_morph`,
|
||||
[definition.collectionName]: function () {
|
||||
return this
|
||||
.belongsTo(
|
||||
GLOBALS[definition.globalId],
|
||||
`${definition.collectionName}_id`
|
||||
);
|
||||
},
|
||||
related: function () {
|
||||
return this.morphTo(name, ...association.related.map((id, index) => [GLOBALS[id], morphValues[index]]));
|
||||
return this
|
||||
.morphTo(name, ...association.related.map((id, index) => [GLOBALS[id], morphValues[index]]));
|
||||
}
|
||||
};
|
||||
|
||||
const MorphModel = ORM.Model.extend(options);
|
||||
GLOBALS[options.tableName] = ORM.Model.extend(options);
|
||||
|
||||
// Hack Bookshelf to create a many-to-many polymorphic association.
|
||||
// Upload has many Upload_morph that morph to different model.
|
||||
loadedModel[name] = function () {
|
||||
if (verbose === 'belongsToMorph') {
|
||||
return this.hasOne(
|
||||
GLOBALS[options.tableName],
|
||||
`${definition.collectionName}_id`
|
||||
);
|
||||
}
|
||||
|
||||
return this.hasMany(
|
||||
MorphModel,
|
||||
'upload_id'
|
||||
GLOBALS[options.tableName],
|
||||
`${definition.collectionName}_id`
|
||||
);
|
||||
};
|
||||
break;
|
||||
|
||||
@ -55,4 +55,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -46,4 +46,4 @@
|
||||
"npm": ">= 5.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -54,7 +54,7 @@ module.exports = strapi => {
|
||||
// Recursive to mask the private properties.
|
||||
const mask = (payload) => {
|
||||
// Handle ORM toJSON() method to work on real JSON object.
|
||||
payload = payload.toJSON ? payload.toJSON() : payload;
|
||||
payload = payload && payload.toJSON ? payload.toJSON() : payload;
|
||||
|
||||
if (_.isArray(payload)) {
|
||||
return payload.map(mask);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user