Skip to content

Commit 5dfbe1c

Browse files
committed
Add 'mapsTo' option when defining hasOne #638
1 parent b8497fb commit 5dfbe1c

File tree

5 files changed

+141
-26
lines changed

5 files changed

+141
-26
lines changed

lib/Associations/Extend.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ exports.prepare = function (db, Model, associations) {
1515
reversed : opts.reversed,
1616
autoFetch : opts.autoFetch || false,
1717
autoFetchLimit : opts.autoFetchLimit || 2,
18-
field : util.wrapFieldObject(opts.field, Model, Model.table, Model.properties) || util.formatField(Model, Model.table, false, false),
18+
field : util.wrapFieldObject({
19+
field: opts.field, model: Model, altName: Model.table
20+
}) || util.formatField(Model, Model.table, false, false),
1921
getAccessor : opts.getAccessor || ("get" + assocName),
2022
setAccessor : opts.setAccessor || ("set" + assocName),
2123
hasAccessor : opts.hasAccessor || ("has" + assocName),

lib/Associations/Many.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ exports.prepare = function (db, Model, associations) {
4444
makeKey = opts.key || Settings.defaults().hasMany.key;
4545

4646
mergeId = util.convertPropToJoinKeyProp(
47-
util.wrapFieldObject(opts.mergeId, Model, Model.table, Model.properties) ||
47+
util.wrapFieldObject({
48+
field: opts.mergeId, model: Model, altName: Model.table
49+
}) ||
4850
util.formatField(Model, Model.table, true, opts.reversed),
4951
{ makeKey: makeKey, required: true }
5052
);
5153

5254
mergeAssocId = util.convertPropToJoinKeyProp(
53-
util.wrapFieldObject(opts.mergeAssocId, OtherModel, name, OtherModel.properties) ||
55+
util.wrapFieldObject({
56+
field: opts.mergeAssocId, model: OtherModel, altName: name
57+
}) ||
5458
util.formatField(OtherModel, name, true, opts.reversed),
5559
{ makeKey: makeKey, required: true }
5660
)
@@ -65,7 +69,10 @@ exports.prepare = function (db, Model, associations) {
6569
autoFetch : opts.autoFetch || false,
6670
autoFetchLimit : opts.autoFetchLimit || 2,
6771
// I'm not sure the next key is used..
68-
field : util.wrapFieldObject(opts.field, OtherModel, Model.table, OtherModel.properties) || util.formatField(Model, name, true, opts.reversed),
72+
field : util.wrapFieldObject({
73+
field: opts.field, model: OtherModel, altName: Model.table
74+
}) ||
75+
util.formatField(Model, name, true, opts.reversed),
6976
mergeTable : opts.mergeTable || (Model.table + "_" + name),
7077
mergeId : mergeId,
7178
mergeAssocId : mergeAssocId,

lib/Associations/One.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ exports.prepare = function (Model, associations) {
3939
if (!association.hasOwnProperty("field")) {
4040
association.field = util.formatField(association.model, association.name, association.required, association.reversed);
4141
} else if(!association.extension) {
42-
association.field = util.wrapFieldObject(association.field, Model, Model.table, Model.properties);
42+
association.field = util.wrapFieldObject({
43+
field: association.field, model: Model, altName: Model.table,
44+
mapsTo: association.mapsTo
45+
});
4346
}
4447

4548
util.convertPropToJoinKeyProp(association.field, {

lib/Utilities.js

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -158,34 +158,43 @@ exports.getConditions = function (model, fields, from) {
158158
return conditions;
159159
};
160160

161-
exports.wrapFieldObject = function (obj, model, altName, alternatives) {
162-
if (!obj) {
163-
var assoc_key = model.settings.get("properties.association_key");
161+
exports.wrapFieldObject = function (params) {
162+
if (!params.field) {
163+
var assoc_key = params.model.settings.get("properties.association_key");
164164

165165
if (typeof assoc_key === "function") {
166-
obj = assoc_key(altName.toLowerCase(), model.id[0]);
166+
params.field = assoc_key(params.altName.toLowerCase(), params.model.id[0]);
167167
} else {
168-
obj = assoc_key.replace("{name}", altName.toLowerCase())
169-
.replace("{field}", model.id[0]);
168+
params.field = assoc_key.replace("{name}", params.altName.toLowerCase())
169+
.replace("{field}", params.model.id[0]);
170170
}
171171
}
172172

173-
for (var k in obj) {
174-
if (!/[0-9]+/.test(k) && obj.hasOwnProperty(k)) {
175-
return obj;
173+
if (typeof params.field == 'object') {
174+
for (var k in params.field) {
175+
if (!/[0-9]+/.test(k) && params.field.hasOwnProperty(k)) {
176+
return params.field;
177+
}
176178
}
177179
}
178180

179-
var new_obj = {};
181+
var newObj = {}, newProp, propPreDefined, propFromKey;
182+
183+
propPreDefined = params.model.properties[params.field];
184+
propFromKey = params.model.properties[params.model.id[0]];
185+
newProp = { type: 'integer' };
180186

181-
new_obj[obj] = _.cloneDeep(
182-
alternatives[obj] || alternatives[model.id[0]] || { type: 'number', unsigned: true, rational: false }
183-
);
184-
new_obj[obj].name = obj;
185-
new_obj[obj].mapsTo = obj;
187+
var prop = _.cloneDeep(propPreDefined || propFromKey || newProp);
188+
189+
if (!propPreDefined) {
190+
_.extend(prop, {
191+
name: params.field, mapsTo: params.mapsTo || params.field
192+
});
193+
}
186194

195+
newObj[params.field] = prop;
187196

188-
return new_obj;
197+
return newObj;
189198
};
190199

191200
exports.formatField = function (model, name, required, reversed) {

test/integration/association-hasone.js

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,27 @@ describe("hasOne", function() {
1212
var leafId = null;
1313
var treeId = null;
1414
var stalkId = null;
15+
var holeId = null;
1516

1617
var setup = function (opts) {
1718
opts = opts || {};
1819
return function (done) {
1920
db.settings.set('instance.cache', false);
2021
db.settings.set('instance.returnAllErrors', true);
21-
Tree = db.define("tree", { type: { type: 'text' } });
22+
Tree = db.define("tree", { type: { type: 'text' } });
2223
Stalk = db.define("stalk", { length: { type: 'integer' } });
24+
Hole = db.define("hole", { width: { type: 'integer' } });
2325
Leaf = db.define("leaf", {
24-
size: { type: 'integer' }
26+
size: { type: 'integer' },
27+
holeId: { type: 'integer', mapsTo: 'hole_id' }
2528
}, {
2629
validations: opts.validations
2730
});
2831
Leaf.hasOne('tree', Tree, { field: 'treeId', autoFetch: !!opts.autoFetch });
29-
Leaf.hasOne('stalk', Stalk, { field: 'stalkId' });
32+
Leaf.hasOne('stalk', Stalk, { field: 'stalkId', mapsTo: 'stalk_id' });
33+
Leaf.hasOne('hole', Hole, { field: 'holeId' });
3034

31-
return helper.dropSync([Tree, Stalk, Leaf], function() {
35+
return helper.dropSync([Tree, Stalk, Hole, Leaf], function() {
3236
Tree.create({ type: 'pine' }, function (err, tree) {
3337
should.not.exist(err);
3438
treeId = tree[Tree.id];
@@ -41,7 +45,11 @@ describe("hasOne", function() {
4145
should.not.exist(err);
4246
should.exist(stalk);
4347
stalkId = stalk[Stalk.id];
44-
done();
48+
Hole.create({ width: 3 }, function (err, hole) {
49+
should.not.exist(err);
50+
holeId = hole.id;
51+
done();
52+
});
4553
});
4654
});
4755
});
@@ -370,4 +378,90 @@ describe("hasOne", function() {
370378
return done();
371379
});
372380
});
381+
382+
describe("mapsTo", function () {
383+
describe("with `mapsTo` set via `hasOne`", function () {
384+
var leaf = null;
385+
386+
before(setup());
387+
388+
before(function (done) {
389+
Leaf.create({ size: 444, stalkId: stalkId, holeId: holeId }, function (err, lf) {
390+
should.not.exist(err);
391+
leaf = lf;
392+
done();
393+
});
394+
});
395+
396+
it("should have correct fields in the DB", function (done) {
397+
var sql = db.driver.query.select()
398+
.from('leaf')
399+
.select('size', 'stalk_id')
400+
.where({ size: 444 })
401+
.build();
402+
403+
db.driver.execQuery(sql, function (err, rows) {
404+
should.not.exist(err);
405+
406+
should.equal(rows[0].size, 444);
407+
should.equal(rows[0].stalk_id, 1);
408+
409+
done();
410+
});
411+
});
412+
413+
it("should get parent", function (done) {
414+
leaf.getStalk(function (err, stalk) {
415+
should.not.exist(err);
416+
417+
should.exist(stalk);
418+
should.equal(stalk.id, stalkId);
419+
should.equal(stalk.length, 20);
420+
done();
421+
});
422+
});
423+
});
424+
425+
describe("with `mapsTo` set via property definition", function () {
426+
var leaf = null;
427+
428+
before(setup());
429+
430+
before(function (done) {
431+
Leaf.create({ size: 444, stalkId: stalkId, holeId: holeId }, function (err, lf) {
432+
should.not.exist(err);
433+
leaf = lf;
434+
done();
435+
});
436+
});
437+
438+
it("should have correct fields in the DB", function (done) {
439+
var sql = db.driver.query.select()
440+
.from('leaf')
441+
.select('size', 'hole_id')
442+
.where({ size: 444 })
443+
.build();
444+
445+
db.driver.execQuery(sql, function (err, rows) {
446+
should.not.exist(err);
447+
448+
should.equal(rows[0].size, 444);
449+
should.equal(rows[0].hole_id, 1);
450+
451+
done();
452+
});
453+
});
454+
455+
it("should get parent", function (done) {
456+
leaf.getHole(function (err, hole) {
457+
should.not.exist(err);
458+
459+
should.exist(hole);
460+
should.equal(hole.id, stalkId);
461+
should.equal(hole.width, 3);
462+
done();
463+
});
464+
});
465+
});
466+
});
373467
});

0 commit comments

Comments
 (0)