Skip to content

Commit 1f38519

Browse files
committed
Intelligent association typing and multikey support
Should allow associations to automatically create foreign keys of the same time as the primary key(s) of the model they reference and also allowing associations to automatically create the required foreign keys for models which have multiple primary key fields. This change requires at least version 0.1.5 of node-sql-query to support the multi-field join functionality used. There is a change in the way chained finds are executed for hasAccessors, meaning that instead of passing the id of the object you pass the entire object. This is to support objects with multiple primary key identifiers. ```js Model.find().hasSomething(TheSomething).all(function(err, somethings) { }); ```
1 parent 5af679a commit 1f38519

File tree

15 files changed

+480
-124
lines changed

15 files changed

+480
-124
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
*.sublime-*
2+
*.njsproj
3+
*.nodevsdbg
24
.DS_Store
35
node_modules
46
test/config.js

Changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
### v2.0.15 - 10 Jul 2013
1+
### v2.0.15 - 10 July 2013
22

33
- Support for 'point' type as a property (#221)
44
- .call() in aggregates for generic functions (#204)

Readme.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,17 @@ var Pet = db.define("pet", {
357357

358358
**Pet** model will have 2 columns, an `UID` and a `name`.
359359

360+
It is also possible to have multiple IDs for a model in the database, this is done by specifying an array of IDs to use.
361+
362+
```js
363+
var Person = db.define("person", {
364+
firstname: String,
365+
lastname: String
366+
}, {
367+
id: ['firstname', 'lastname']
368+
});
369+
```
370+
360371
Other options:
361372

362373
- `cache` : (default: `true`) Set it to `false` to disable Instance cache ([Singletons](#singleton)) or set a timeout value (in seconds);
@@ -922,7 +933,7 @@ Animal.hasOne("owner", Person, { required: true });
922933
If you prefer to use another name for the field (owner_id) you can change this parameter in the settings.
923934

924935
```js
925-
db.settings.set("properties.association_key", "id_{name}"); // {name} will be replaced by 'owner' in this case
936+
db.settings.set("properties.association_key", "{field}_{name}"); // {name} will be replaced by 'owner' and {field} will be replaced by 'id' in this case
926937
```
927938

928939
**Note: This has to be done before the association is specified.**

lib/Associations/Extend.js

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ exports.prepare = function (db, Model, associations, association_properties, mod
1212
reversed : opts.reversed,
1313
autoFetch : opts.autoFetch || false,
1414
autoFetchLimit : opts.autoFetchLimit || 2,
15-
field : opts.field || Model.settings.get("properties.association_key").replace("{name}", Model.table),
15+
field : wrapFieldObject(opts.field, Model, []) || formatField(Model, Model.table, false),
1616
getAccessor : opts.getAccessor || ("get" + assocName),
1717
setAccessor : opts.setAccessor || ("set" + assocName),
1818
hasAccessor : opts.hasAccessor || ("has" + assocName),
@@ -21,10 +21,10 @@ exports.prepare = function (db, Model, associations, association_properties, mod
2121

2222
association.model = db.define(Model.table + "_" + name, properties, {
2323
id : null,
24-
keys : [ association.field ],
24+
keys : getKeys(association.field),
2525
extension : true
2626
});
27-
association.model.hasOne(Model.table, Model, { extension: true });
27+
association.model.hasOne(Model.table, Model, { extension: true, field: association.field });
2828

2929
associations.push(association);
3030

@@ -57,13 +57,80 @@ exports.autoFetch = function (Instance, associations, opts, cb) {
5757
}
5858
};
5959

60+
function getKeys(arr) {
61+
var keys = [];
62+
for (k in arr)
63+
keys.push(k);
64+
return keys;
65+
}
66+
67+
function wrapFieldObject(obj, Model, alternatives) {
68+
if (!obj) {
69+
obj = Model.settings.get("properties.association_key").replace("{name}", Model.table.toLowerCase()).replace("{field}", "id");
70+
}
71+
isvalid = false;
72+
for (k in obj) {
73+
if (!/[0-9]+/.test(k) && obj.hasOwnProperty(k)) isvalid = true;
74+
}
75+
if (isvalid) return obj;
76+
77+
newobj = {};
78+
newobj[obj] = alternatives[obj] || alternatives[Model.keys[0]] || { type: 'number', unsigned: true, rational: false };
79+
return newobj;
80+
}
81+
82+
function formatField(Model, name, required) {
83+
var fields = {};
84+
var keys = ["id"];
85+
if (Model.keys instanceof Array) {
86+
keys = Model.keys;
87+
}
88+
else {
89+
keys = [Model.keys];
90+
}
91+
92+
for (var i = 0; i < keys.length; i++) {
93+
var fieldName = Model.settings.get("properties.association_key").replace("{name}", name.toLowerCase()).replace("{field}", Model.keys[i]);
94+
var fieldOpts = {
95+
type: "number",
96+
unsigned: true,
97+
size: 4
98+
};
99+
100+
if (Model.properties.hasOwnProperty(Model.keys[i])) {
101+
var p = Model.properties[Model.keys[i]];
102+
fieldOpts = {
103+
type: p.type || "number",
104+
size: p.size || 4,
105+
rational: p.rational || false,
106+
unsigned: p.unsigned || true,
107+
time: p.time || false,
108+
big: p.big || false,
109+
values: p.values || null
110+
};
111+
};
112+
113+
fields[fieldName] = fieldOpts;
114+
}
115+
116+
return fields;
117+
}
118+
119+
function makeConditions(Instance, Model) {
120+
var conditions = [];
121+
for (i = 0; i < Model.keys.length; i++) {
122+
conditions.push(Instance[Model.keys[i]]);
123+
}
124+
return conditions;
125+
}
126+
60127
function extendInstance(Model, Instance, Driver, association, opts) {
61128
Object.defineProperty(Instance, association.hasAccessor, {
62129
value : function (cb) {
63130
if (!Instance[Model.id]) {
64131
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension"));
65132
} else {
66-
association.model.get(Instance[Model.id], function (err, extension) {
133+
association.model.get(makeConditions(Instance, Model), function (err, extension) {
67134
return cb(err, !err && extension ? true : false);
68135
});
69136
}
@@ -72,11 +139,11 @@ function extendInstance(Model, Instance, Driver, association, opts) {
72139
enumerable : false
73140
});
74141
Object.defineProperty(Instance, association.getAccessor, {
75-
value : function (cb) {
142+
value: function (cb) {
76143
if (!Instance[Model.id]) {
77144
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension"));
78145
} else {
79-
association.model.get(Instance[Model.id], cb);
146+
association.model.get(makeConditions(Instance, Model), cb);
80147
}
81148
return this;
82149
},
@@ -94,7 +161,11 @@ function extendInstance(Model, Instance, Driver, association, opts) {
94161
return cb(err);
95162
}
96163

97-
Extension[association.field] = Instance[Model.id];
164+
var fields = getKeys(association.field);
165+
for (i = 0; i < Model.keys.length; i++) {
166+
Extension[fields[i]] = Instance[Model.keys[i]];
167+
}
168+
98169
Extension.save(cb);
99170
});
100171
});
@@ -108,7 +179,10 @@ function extendInstance(Model, Instance, Driver, association, opts) {
108179
cb(ErrorCodes.generateError(ErrorCodes.NOT_DEFINED, "Instance not saved, cannot get extension"));
109180
} else {
110181
var conditions = {};
111-
conditions[association.field] = Instance[Model.id];
182+
var fields = getKeys(association.field);
183+
for (i = 0; i < Model.keys.length; i++) {
184+
conditions[fields[i]] = Instance[Model.keys[i]];
185+
}
112186

113187
association.model.find(conditions, function (err, extensions) {
114188
if (err) {

0 commit comments

Comments
 (0)