1
1
const { Knorm } = require ( '@knorm/knorm' ) ;
2
2
const { camelCase } = require ( 'lodash' ) ;
3
3
4
+ const isArray = Array . isArray ;
5
+
4
6
const addReference = ( references , field , reference ) => {
5
7
const toModel = reference . model ;
6
8
references [ toModel . name ] = references [ toModel . name ] || { } ;
7
9
references [ toModel . name ] [ field . name ] = field ;
8
10
} ;
9
11
10
12
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
+ } ) ;
15
23
} ;
16
24
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
+ } ) ;
22
37
return referencesByTo ;
23
38
} , { } ) ;
39
+ } ;
24
40
25
41
class KnormRelations {
26
42
constructor ( { name = 'relations' } = { } ) {
@@ -57,12 +73,16 @@ class KnormRelations {
57
73
super . addField ( field ) ;
58
74
59
75
if ( field . references ) {
60
- const toField = field . references ;
76
+ const references = field . references ;
61
77
62
- if ( typeof toField === 'function' ) {
63
- this . _config . referenceFunctions [ field . name ] = toField ;
78
+ if ( typeof references === 'function' ) {
79
+ this . _config . referenceFunctions [ field . name ] = references ;
64
80
} 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
+ ) ;
66
86
}
67
87
}
68
88
}
@@ -103,7 +123,7 @@ class KnormRelations {
103
123
}
104
124
105
125
addJoin ( type , joins , options ) {
106
- if ( ! Array . isArray ( joins ) ) {
126
+ if ( ! isArray ( joins ) ) {
107
127
joins = [ joins ] ;
108
128
}
109
129
@@ -142,16 +162,18 @@ class KnormRelations {
142
162
) ;
143
163
}
144
164
165
+ const isReverseJoin = ! ! thisReferences [ join . model . name ] ;
145
166
const mergedReferences = Object . assign (
146
167
{ } ,
147
168
thisReferences [ join . model . name ] ,
148
169
joinReferences [ this . model . name ]
149
170
) ;
150
171
const mergedReferencesReversed = mapReferencesByReferencedField (
151
- mergedReferences
172
+ mergedReferences ,
173
+ isReverseJoin ? join . model : this . model
152
174
) ;
153
175
154
- join . config . isReverseJoin = ! ! thisReferences [ join . model . name ] ;
176
+ join . config . isReverseJoin = isReverseJoin ;
155
177
join . config . mergedReferences = mergedReferences ;
156
178
join . config . mergedReferencesReversed = mergedReferencesReversed ;
157
179
@@ -177,7 +199,8 @@ class KnormRelations {
177
199
return this . setOption ( 'as' , as ) ;
178
200
}
179
201
180
- // TODO: support multiple fields for `on`
202
+ // TODO: v2: support multiple fields for `on` via Query.prototpye.appendOption
203
+ // TODO: support raw sql
181
204
on ( field ) {
182
205
return this . addOption ( 'on' , field ) ;
183
206
}
@@ -213,27 +236,38 @@ class KnormRelations {
213
236
references = Object . values ( this . config . mergedReferences ) ;
214
237
}
215
238
239
+ const isReverseJoin = this . config . isReverseJoin ;
240
+
216
241
return references . reduce ( ( columns , field ) => {
217
242
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
+ }
229
261
230
- columns [ from ] = to ;
262
+ columns [ from ] = to ;
263
+ }
264
+ } ) ;
231
265
232
266
return columns ;
233
267
} , { } ) ;
234
268
}
235
269
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)
237
271
async prepareJoin ( sql , options ) {
238
272
const method = this . options . joinType || 'leftJoin' ;
239
273
sql [ method ] ( this . getTable ( ) , this . prepareOn ( ) ) ;
@@ -360,7 +394,7 @@ class KnormRelations {
360
394
parsedRow [ as ] = data ;
361
395
}
362
396
} else {
363
- if ( ! Array . isArray ( parsedRow [ as ] ) ) {
397
+ if ( ! isArray ( parsedRow [ as ] ) ) {
364
398
parsedRow [ as ] = [ ] ;
365
399
}
366
400
parsedRow [ as ] . push ( data ) ;
0 commit comments