index.js
2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
* Module dependencies.
*/
import { mapSeries } from 'bluebird';
import { flattenDeep, reduce } from 'lodash';
/**
* Export `bookshelf-cascade-delete` plugin.
*/
export default Bookshelf => {
const Model = Bookshelf.Model.prototype;
const knex = Bookshelf.knex;
const client = knex.client.config.client;
const quoteColumns = client === 'postgres' || client === 'postgresql' || client === 'pg';
/**
* Dependency map.
*/
function dependencyMap(skipDependents = false) {
if (skipDependents || !this.dependents) {
return;
}
return reduce(this.dependents, (result, dependent) => {
const { relatedData } = this.prototype[dependent]();
const skipDependents = relatedData.type === 'belongsToMany';
return [
...result, {
dependents: dependencyMap.call(relatedData.target, skipDependents),
key: relatedData.key('foreignKey'),
model: relatedData.target,
skipDependents,
tableName: skipDependents ? relatedData.joinTable() : relatedData.target.prototype.tableName
}
];
}, []);
}
/**
* Recursive deletes.
*/
function recursiveDeletes(parent) {
// Stringify in case of parent being an instance of query.
const parentValue = typeof parent === 'number' || typeof parent === 'string' ? `'${parent}'` : parent.toString();
const dependencies = dependencyMap.call(this);
// Build delete queries for each dependent.
return reduce(dependencies, (result, { tableName, key, model, skipDependents }) => {
const whereClause = `${quoteColumns ? `"${key}"` : key} IN (${parentValue})`;
return [
...result,
transaction => transaction(tableName).del().whereRaw(whereClause),
skipDependents ? [] : recursiveDeletes.call(model, knex(tableName).column(model.prototype.idAttribute).whereRaw(whereClause))
];
}, []);
}
/**
* Cascade delete.
*/
function cascadeDelete(transacting, options) {
const id = this.get(this.idAttribute) || this._knex.column(this.idAttribute);
const queries = recursiveDeletes.call(this.constructor, id);
return mapSeries(flattenDeep(queries).reverse(), query => query(transacting))
.then(() => Model.destroy.call(this, {
...options,
transacting
}));
}
/**
* Extend Model `destroy` method.
*/
Bookshelf.Model = Bookshelf.Model.extend({
destroy(options) {
options = options || {};
if (options.cascadeDelete === false) {
return Model.destroy.call(this, options);
}
if (options.transacting) {
return cascadeDelete.call(this, options.transacting, options);
}
return Bookshelf.knex.transaction(transacting => cascadeDelete.call(this, transacting, options));
}
});
};