Skip to content

Commit 488faf6

Browse files
committed
Add rows to hasMany join table in order provided
Previously, they were added in reverse order
1 parent 8c53f2e commit 488faf6

File tree

7 files changed

+68
-69
lines changed

7 files changed

+68
-69
lines changed

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### v8.0.0
2+
- [POSSIBLY BREAKING] Add rows to hasMany join table in order provided (previously, they were added in reverse order) ([862](../../pull/862))
3+
14
### v7.1.1
25
- Resolve most development-only security vulnerabilities (no production ones present) ([861](../../pull/861))
36
- Don't run test on node 10 & 12 - dev deps don't work

lib/Associations/Many.js

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var _ = require("lodash");
2+
var async = require("async");
23
var Hook = require("../Hook");
34
var Settings = require("../Settings");
45
var Property = require("../Property");
@@ -369,17 +370,13 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan
369370
value: function () {
370371
var Associations = [];
371372
var opts = {};
372-
var cb = noOperation;
373+
var next = noOperation;
373374

374375
var run = function () {
375-
var savedAssociations = [];
376-
var saveNextAssociation = function () {
377-
if (Associations.length === 0) {
378-
return cb(null, savedAssociations);
379-
}
376+
var saveAssociation = function (Association, cb) {
377+
const hookOpts = Object.keys(association.props).length > 0 ? opts : undefined;
380378

381-
var Association = Associations.pop();
382-
var saveAssociation = function (err) {
379+
Hook.wait(Association, association.hooks.beforeSave, function (err) {
383380
if (err) {
384381
return cb(err);
385382
}
@@ -405,9 +402,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan
405402
return cb(err);
406403
}
407404

408-
savedAssociations.push(Association);
409-
410-
return saveNextAssociation();
405+
return cb();
411406
});
412407
}
413408

@@ -419,27 +414,24 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan
419414
return cb(err);
420415
}
421416

422-
savedAssociations.push(Association);
423-
424-
return saveNextAssociation();
417+
return cb();
425418
});
426419
});
427-
};
428-
429-
if (Object.keys(association.props).length) {
430-
Hook.wait(Association, association.hooks.beforeSave, saveAssociation, opts);
431-
} else {
432-
Hook.wait(Association, association.hooks.beforeSave, saveAssociation);
433-
}
420+
}, hookOpts);
434421
};
435422

436-
return saveNextAssociation();
423+
async.eachSeries(Associations, saveAssociation, function (err) {
424+
if (err) {
425+
return next(err);
426+
}
427+
next(null, Associations);
428+
});
437429
};
438430

439431
for (var i = 0; i < arguments.length; i++) {
440432
switch (typeof arguments[i]) {
441433
case "function":
442-
cb = arguments[i];
434+
next = arguments[i];
443435
break;
444436
case "object":
445437
if (Array.isArray(arguments[i])) {
@@ -462,7 +454,7 @@ function extendInstance(Model, Instance, Driver, association, opts, createInstan
462454
} else {
463455
this.save(function (err) {
464456
if (err) {
465-
return cb(err);
457+
return next(err);
466458
}
467459

468460
return run();

lib/Associations/One.js

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
var _ = require("lodash");
2+
var async = require("async");
23
var util = require("../Utilities");
34
var ORMError = require("../Error");
45
var Accessors = { "get": "get", "set": "set", "has": "has", "del": "remove" };
@@ -214,52 +215,39 @@ function extendInstance(Model, Instance, Driver, association) {
214215
writable: true
215216
});
216217
Object.defineProperty(Instance, association.setAccessor, {
217-
value: function (OtherInstance, cb) {
218+
value: function (OtherInstance, next) {
218219
if (association.reversed) {
219220
Instance.save(function (err) {
220221
if (err) {
221-
return cb(err);
222+
return next(err);
222223
}
223224

224225
if (!Array.isArray(OtherInstance)) {
225226
util.populateConditions(Model, Object.keys(association.field), Instance, OtherInstance, true);
226227

227-
return OtherInstance.save({}, { saveAssociations: false }, cb);
228+
return OtherInstance.save({}, { saveAssociations: false }, next);
228229
}
229230

230-
var associations = _.clone(OtherInstance);
231-
232-
var saveNext = function () {
233-
if (!associations.length) {
234-
return cb();
235-
}
236-
237-
var other = associations.pop();
231+
var saveAssociation = function (otherInstance, cb) {
232+
util.populateConditions(Model, Object.keys(association.field), Instance, otherInstance, true);
238233

239-
util.populateConditions(Model, Object.keys(association.field), Instance, other, true);
240-
241-
other.save({}, { saveAssociations: false }, function (err) {
242-
if (err) {
243-
return cb(err);
244-
}
245-
246-
saveNext();
247-
});
234+
otherInstance.save({}, { saveAssociations: false }, cb);
248235
};
249236

250-
return saveNext();
237+
var associations = _.clone(OtherInstance);
238+
async.eachSeries(associations, saveAssociation, next);
251239
});
252240
} else {
253241
OtherInstance.save({}, { saveAssociations: false }, function (err) {
254242
if (err) {
255-
return cb(err);
243+
return next(err);
256244
}
257245

258246
Instance[association.name] = OtherInstance;
259247

260248
util.populateConditions(association.model, Object.keys(association.field), OtherInstance, Instance);
261249

262-
return Instance.save({}, { saveAssociations: false }, cb);
250+
return Instance.save({}, { saveAssociations: false }, next);
263251
});
264252
}
265253

lib/Hook.js

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,30 @@ exports.trigger = function () {
88
}
99
};
1010

11-
exports.wait = function () {
12-
var args = Array.prototype.slice.apply(arguments);
13-
var self = args.shift();
14-
var hook = args.shift();
15-
var next = args.shift();
11+
exports.wait = function (self, hook, next, opts) {
12+
if (typeof hook !== "function") {
13+
return next();
14+
}
1615

17-
args.push(next);
18-
if (typeof hook === "function") {
19-
var hookValue = hook.apply(self, args);
16+
var hookDoesntExpectCallback = hook.length < 1;
17+
var hookValue;
18+
if (opts) {
19+
hookValue = hook.call(self, opts, next);
20+
hookDoesntExpectCallback = hook.length < 2;
21+
} else {
22+
hookValue = hook.call(self, next);
23+
}
2024

21-
var hookDoesntExpectCallback = hook.length < args.length;
22-
var isPromise = hookValue && typeof(hookValue.then) === "function";
25+
var isPromise = hookValue && typeof(hookValue.then) === "function";
2326

24-
if (hookDoesntExpectCallback) {
25-
if (isPromise) {
26-
return hookValue
27-
.then(function () {
28-
next();
29-
})
30-
.catch(next);
31-
}
32-
return next();
27+
if (hookDoesntExpectCallback) {
28+
if (isPromise) {
29+
return hookValue
30+
.then(function () {
31+
next();
32+
})
33+
.catch(next);
3334
}
34-
} else {
3535
return next();
3636
}
3737
};

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"sqlite",
1313
"mongodb"
1414
],
15-
"version": "7.1.1",
15+
"version": "8.0.0",
1616
"license": "MIT",
1717
"homepage": "http://dresende.github.io/node-orm2",
1818
"repository": "http://github.com/dresende/node-orm2.git",

test/integration/association-hasmany.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,22 @@ describe("hasMany", function () {
513513
});
514514
});
515515

516+
it("should add rows to join tables in order provided", async function () {
517+
const pets = await Pet.createAsync([{ name: 'Fluffy' }, { name: 'Quacky' }, { name: 'Horsey' }]);
518+
const Justin = await Person.oneAsync({ name: "Justin" });
519+
const justinsPetsBefore = await Justin.getPetsAsync();
520+
await Justin.addPetsAsync(pets);
521+
const justinsPetsAfter = await Justin.getPetsAsync();
522+
523+
should.equal(justinsPetsBefore.length, justinsPetsAfter.length - 3);
524+
525+
const joinRows = await db.driver.execQueryAsync("SELECT * FROM person_pets");
526+
527+
joinRows.slice(-pets.length).should.eql(
528+
pets.map(function (pet) { return { person_id: Justin.id, pets_id: pet.id }; })
529+
);
530+
});
531+
516532
it("should throw if no items passed", function (done) {
517533
Person.one(function (err, person) {
518534
should.equal(err, null);

0 commit comments

Comments
 (0)