Skip to content

Instantly share code, notes, and snippets.

@zbarbuto
Forked from CjS77/cascade.js
Last active April 3, 2021 22:20
Show Gist options
  • Save zbarbuto/add938efd9653c7c6c14 to your computer and use it in GitHub Desktop.
Save zbarbuto/add938efd9653c7c6c14 to your computer and use it in GitHub Desktop.
Loopback cascade mixin gist
// Cascade delete for loopback
// https://gist.github.com/CjS77/44b2f75c0ec468f590d0
/* jshint node:true */
'use strict';
/**
* There is an incubating feature to cascade the deletes to the relational tables. see
* https://github.com/strongloop/loopback-datasource-juggler/issues/88
*/
var lazy = require('lazy.js');
var async = require('async');
var log = require('debug')('mixins:cascade');
var Q = require('q');
module.exports = function (Model, options) {
var matchRelation = function (name) {
return lazy(Model.relations).find(function (mr) {
return mr.name === name;
});
};
//Pre-process and remove unsupported relations
lazy(options).keys().forEach(function (relationName) {
var relation = matchRelation(relationName);
if (relation && relation.type === 'referencesMany') {
log("Cascading to referencesMany is not supported - %s", relation.name);
delete options[relationName];
}
});
Model.observe('after delete', function (ctx, next) {
if (!(ctx.instance || ctx.where)) {
log("There is no way to apply cascading deletes on a multi-record delete");
return; //Nothing we can do
}
var promises = [];
var thisId = ctx.instance ? ctx.instance.id : ctx.where.id;
lazy(options).keys().each(function (relationName) {
var relation = matchRelation(relationName);
if (!relation) {
return;
}
var filter = {};
filter[relation.keyTo] = thisId;
if (options[relationName].unlink) {
var throughModel = relation.modelThrough;
if (throughModel) {
breakLinks(throughModel, filter, next);
} else {
log("%s.%s does not have a through model. Nothing to unlink", Model.definition.name, relationName);
}
} else {
//See https://gist.github.com/fabien/126ccfaca48ddf1cefb8
var promise = relation.modelTo.find({where: filter}).then(function (items) {
return Q.all(
lazy(items).map(function (inst) {
return inst.destroy();
}).toArray()
).then(function (results) {
log("Cascaded delete of: ", items);
if (relation.modelThrough) {
breakLinks(relation.modelThrough, filter, next);
}
});
});
promises.push(promise);
}
});
Promise.all(promises).then(function(results) {
log('Cascade: ' + results);
next();
});
});
function breakLinks(throughModel, filter, next) {
throughModel.destroyAll(filter).then(function (info) {
log("Removed %d links from %s referring to %s", info.count, throughModel.definition.name, Model.definition.name);
next();
}).catch(function (err) {
next(err);
});
}
};
@zbarbuto
Copy link
Author

Fixed an issue in the original which would cause only the first relation to be deleted (because the first resolved delete would call next()).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment