Skip to content
This repository was archived by the owner on Apr 17, 2020. It is now read-only.

Commit 9319531

Browse files
committed
feature: support multiple references
```js class Foo extends Model {} Foo.fields = { id: 'integer' }; class Bar extends Model {} Bar.fields = { id: 'integer' }; class Quux extends Model {} Foo.fields = { refId: { type: 'integer', // multiple references can be defined as an array // or as a function that returns an array references: [Foo.fields.id, Bar.fields.id] } }; // these fetches would now supported: Quux.fetch({ join: Foo }); Quux.fetch({ join: Bar }); Foo.fetch({ join: Quux }); Bar.fetch({ join: Quux }); ```
1 parent 8a00da6 commit 9319531

File tree

2 files changed

+785
-63
lines changed

2 files changed

+785
-63
lines changed

lib/KnormRelations.js

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,42 @@
11
const { Knorm } = require('@knorm/knorm');
22
const { camelCase } = require('lodash');
33

4+
const isArray = Array.isArray;
5+
46
const addReference = (references, field, reference) => {
57
const toModel = reference.model;
68
references[toModel.name] = references[toModel.name] || {};
79
references[toModel.name][field.name] = field;
810
};
911

1012
const addReferenceByFunction = (references, func, { name, column }) => {
11-
const reference = func();
12-
// add a pseudo-field to avoid having to overwrite field.references
13-
const field = { name, column, references: reference };
14-
addReference(references, field, reference);
13+
let resolvedReferences = func();
14+
resolvedReferences = isArray(resolvedReferences)
15+
? resolvedReferences
16+
: [resolvedReferences];
17+
18+
resolvedReferences.forEach(reference => {
19+
// add a pseudo-field to avoid having to overwrite field.references
20+
const field = { name, column, references: reference };
21+
addReference(references, field, reference);
22+
});
1523
};
1624

17-
const mapReferencesByReferencedField = references =>
18-
Object.values(references).reduce((referencesByTo, from) => {
19-
const to = from.references.name;
20-
referencesByTo[to] = referencesByTo[to] || [];
21-
referencesByTo[to].push(from);
25+
const mapReferencesByReferencedField = (references, fromModel) => {
26+
return Object.values(references).reduce((referencesByTo, from) => {
27+
const references = isArray(from.references)
28+
? from.references
29+
: [from.references];
30+
references.forEach(reference => {
31+
if (reference.model.name === fromModel.name) {
32+
const to = reference.name;
33+
referencesByTo[to] = referencesByTo[to] || [];
34+
referencesByTo[to].push(from);
35+
}
36+
});
2237
return referencesByTo;
2338
}, {});
39+
};
2440

2541
class KnormRelations {
2642
constructor({ name = 'relations' } = {}) {
@@ -57,12 +73,16 @@ class KnormRelations {
5773
super.addField(field);
5874

5975
if (field.references) {
60-
const toField = field.references;
76+
const references = field.references;
6177

62-
if (typeof toField === 'function') {
63-
this._config.referenceFunctions[field.name] = toField;
78+
if (typeof references === 'function') {
79+
this._config.referenceFunctions[field.name] = references;
6480
} else {
65-
addReference(this._config.references, field, toField);
81+
(isArray(references) ? references : [references]).forEach(
82+
reference => {
83+
addReference(this._config.references, field, reference);
84+
}
85+
);
6686
}
6787
}
6888
}
@@ -103,7 +123,7 @@ class KnormRelations {
103123
}
104124

105125
addJoin(type, joins, options) {
106-
if (!Array.isArray(joins)) {
126+
if (!isArray(joins)) {
107127
joins = [joins];
108128
}
109129

@@ -142,16 +162,18 @@ class KnormRelations {
142162
);
143163
}
144164

165+
const isReverseJoin = !!thisReferences[join.model.name];
145166
const mergedReferences = Object.assign(
146167
{},
147168
thisReferences[join.model.name],
148169
joinReferences[this.model.name]
149170
);
150171
const mergedReferencesReversed = mapReferencesByReferencedField(
151-
mergedReferences
172+
mergedReferences,
173+
isReverseJoin ? join.model : this.model
152174
);
153175

154-
join.config.isReverseJoin = !!thisReferences[join.model.name];
176+
join.config.isReverseJoin = isReverseJoin;
155177
join.config.mergedReferences = mergedReferences;
156178
join.config.mergedReferencesReversed = mergedReferencesReversed;
157179

@@ -177,7 +199,8 @@ class KnormRelations {
177199
return this.setOption('as', as);
178200
}
179201

180-
// TODO: support multiple fields for `on`
202+
// TODO: v2: support multiple fields for `on` via Query.prototpye.appendOption
203+
// TODO: support raw sql
181204
on(field) {
182205
return this.addOption('on', field);
183206
}
@@ -213,27 +236,38 @@ class KnormRelations {
213236
references = Object.values(this.config.mergedReferences);
214237
}
215238

239+
const isReverseJoin = this.config.isReverseJoin;
240+
216241
return references.reduce((columns, field) => {
217242
const fromColumn = field.column;
218-
const toColumn = field.references.column;
219-
let from;
220-
let to;
221-
222-
if (this.config.isReverseJoin) {
223-
from = this.formatColumn(toColumn);
224-
to = this.parent.formatColumn(fromColumn);
225-
} else {
226-
from = this.formatColumn(fromColumn);
227-
to = this.parent.formatColumn(toColumn);
228-
}
243+
const references = isArray(field.references)
244+
? field.references
245+
: [field.references];
246+
247+
references.forEach(reference => {
248+
const toModel = isReverseJoin ? this.model : this.parent.model;
249+
if (reference.model.name === toModel.name) {
250+
const toColumn = reference.column;
251+
let from;
252+
let to;
253+
254+
if (this.config.isReverseJoin) {
255+
from = this.formatColumn(toColumn);
256+
to = this.parent.formatColumn(fromColumn);
257+
} else {
258+
from = this.formatColumn(fromColumn);
259+
to = this.parent.formatColumn(toColumn);
260+
}
229261

230-
columns[from] = to;
262+
columns[from] = to;
263+
}
264+
});
231265

232266
return columns;
233267
}, {});
234268
}
235269

236-
// TODO: do not rely on `getTable` auto-aliasing (@knorm/knorm v2)
270+
// TODO: v2: do not rely on `getTable` auto-aliasing (@knorm/knorm v2)
237271
async prepareJoin(sql, options) {
238272
const method = this.options.joinType || 'leftJoin';
239273
sql[method](this.getTable(), this.prepareOn());
@@ -360,7 +394,7 @@ class KnormRelations {
360394
parsedRow[as] = data;
361395
}
362396
} else {
363-
if (!Array.isArray(parsedRow[as])) {
397+
if (!isArray(parsedRow[as])) {
364398
parsedRow[as] = [];
365399
}
366400
parsedRow[as].push(data);

0 commit comments

Comments
 (0)