@@ -3,6 +3,7 @@ import { attachPaginate } from 'knex-paginate'
33import querystring from ' querystring'
44import location from ' ../Support/Helpers/location'
55import isString from ' ../Support/Helpers/isString'
6+ import singularize from ' ../Support/Helpers/singularize'
67import Config from ' ./Config'
78import knex from ' knex'
89
3738 for column in columns
3839 object [column ] = result [column ]
3940 mappedResults .push (object )
40- return mappedResults
41+ results = mappedResults
4142
4243 if this ._hidden && Array .isArray (this ._hidden ) && this ._hidden .length > 0
4344 for result in results
4445 for column in this ._hidden
4546 delete result [column ]
4647
48+ if this ._relationships && Array .isArray (this ._relationships ) && this ._relationships .length > 0
49+ results = await this ._loadRelationships (results )
50+
4751 results
4852
4953 knex .QueryBuilder .extend ' autoPaginate' , do (pageSize = 20 )
109113 for column in this ._hidden
110114 delete result [column ]
111115
116+ if this ._relationships && Array .isArray (this ._relationships ) && this ._relationships .length > 0
117+ data = await this ._loadRelationships (data )
118+
112119 const results = {
113120 data ,
114121 pagination: {
@@ -190,6 +197,304 @@ try
190197
191198 return this
192199
200+ knex .QueryBuilder .extend ' belongsTo' , do (relatedTable , queryCallback , foreignKey , localKey )
201+ this ._relationships = this ._relationships || []
202+
203+ let tableName = relatedTable
204+ if typeof relatedTable == ' function' && relatedTable .prototype && relatedTable .prototype .tableName
205+ tableName = new relatedTable ().tableName
206+
207+ if typeof queryCallback == ' function'
208+ this ._relationships .push ({
209+ type: ' belongsTo' ,
210+ relatedTable: tableName ,
211+ queryCallback: queryCallback ,
212+ foreignKey: foreignKey || singularize (tableName ) + ' _id' ,
213+ localKey: localKey || ' id'
214+ })
215+ else if typeof queryCallback == ' string'
216+ this ._relationships .push ({
217+ type: ' belongsTo' ,
218+ relatedTable: tableName ,
219+ queryCallback: null ,
220+ foreignKey: queryCallback ,
221+ localKey: foreignKey || ' id'
222+ })
223+ else
224+ this ._relationships .push ({
225+ type: ' belongsTo' ,
226+ relatedTable: tableName ,
227+ queryCallback: null ,
228+ foreignKey: singularize (tableName ) + ' _id' ,
229+ localKey: ' id'
230+ })
231+
232+ return this
233+
234+ knex .QueryBuilder .extend ' hasOne' , do (relatedTable , queryCallback , foreignKey , localKey )
235+ this ._relationships = this ._relationships || []
236+
237+ let tableName = relatedTable
238+ if typeof relatedTable == ' function' && relatedTable .prototype && relatedTable .prototype .tableName
239+ tableName = new relatedTable ().tableName
240+
241+ if typeof queryCallback == ' function'
242+ this ._relationships .push ({
243+ type: ' hasOne' ,
244+ relatedTable: tableName ,
245+ queryCallback: queryCallback ,
246+ foreignKey: foreignKey || singularize (this ._single .table ) + ' _id' ,
247+ localKey: localKey || ' id'
248+ })
249+ else if typeof queryCallback == ' string'
250+ this ._relationships .push ({
251+ type: ' hasOne' ,
252+ relatedTable: tableName ,
253+ queryCallback: null ,
254+ foreignKey: queryCallback ,
255+ localKey: foreignKey || ' id'
256+ })
257+ else
258+ this ._relationships .push ({
259+ type: ' hasOne' ,
260+ relatedTable: tableName ,
261+ queryCallback: null ,
262+ foreignKey: singularize (this ._single .table ) + ' _id' ,
263+ localKey: ' id'
264+ })
265+
266+ return this
267+
268+ knex .QueryBuilder .extend ' hasMany' , do (relatedTable , queryCallback , foreignKey , localKey )
269+ this ._relationships = this ._relationships || []
270+
271+ let tableName = relatedTable
272+ if typeof relatedTable == ' function' && relatedTable .prototype && relatedTable .prototype .tableName
273+ tableName = new relatedTable ().tableName
274+
275+ if typeof queryCallback == ' function'
276+ this ._relationships .push ({
277+ type: ' hasMany' ,
278+ relatedTable: tableName ,
279+ queryCallback: queryCallback ,
280+ foreignKey: foreignKey || singularize (this ._single .table ) + ' _id' ,
281+ localKey: localKey || ' id'
282+ })
283+ else if typeof queryCallback == ' string'
284+ this ._relationships .push ({
285+ type: ' hasMany' ,
286+ relatedTable: tableName ,
287+ queryCallback: null ,
288+ foreignKey: queryCallback ,
289+ localKey: foreignKey || ' id'
290+ })
291+ else
292+ this ._relationships .push ({
293+ type: ' hasMany' ,
294+ relatedTable: tableName ,
295+ queryCallback: null ,
296+ foreignKey: singularize (this ._single .table ) + ' _id' ,
297+ localKey: ' id'
298+ })
299+
300+ return this
301+
302+ knex .QueryBuilder .extend ' belongsToMany' , do (relatedTable , queryCallback , pivotTable , foreignKey , relatedKey , localKey , relatedLocalKey )
303+ this ._relationships = this ._relationships || []
304+
305+ let tableName = relatedTable
306+ if typeof relatedTable == ' function' && relatedTable .prototype && relatedTable .prototype .tableName
307+ tableName = new relatedTable ().tableName
308+
309+ if typeof queryCallback == ' function'
310+ this ._relationships .push ({
311+ type: ' belongsToMany' ,
312+ relatedTable: tableName ,
313+ queryCallback: queryCallback ,
314+ pivotTable: pivotTable || singularize (this ._single .table ) + ' _' + tableName ,
315+ foreignKey: foreignKey || singularize (this ._single .table ) + ' _id' ,
316+ relatedKey: relatedKey || singularize (tableName ) + ' _id' ,
317+ localKey: localKey || ' id' ,
318+ relatedLocalKey: relatedLocalKey || ' id'
319+ })
320+ else if typeof queryCallback == ' string'
321+ this ._relationships .push ({
322+ type: ' belongsToMany' ,
323+ relatedTable: tableName ,
324+ queryCallback: null ,
325+ pivotTable: queryCallback ,
326+ foreignKey: foreignKey || singularize (this ._single .table ) + ' _id' ,
327+ relatedKey: relatedKey || singularize (tableName ) + ' _id' ,
328+ localKey: localKey || ' id' ,
329+ relatedLocalKey: relatedLocalKey || ' id'
330+ })
331+ else
332+ this ._relationships .push ({
333+ type: ' belongsToMany' ,
334+ relatedTable: tableName ,
335+ queryCallback: null ,
336+ pivotTable: singularize (this ._single .table ) + ' _' + tableName ,
337+ foreignKey: singularize (this ._single .table ) + ' _id' ,
338+ relatedKey: singularize (tableName ) + ' _id' ,
339+ localKey: ' id' ,
340+ relatedLocalKey: ' id'
341+ })
342+
343+ return this
344+
345+ knex .QueryBuilder .extend ' _loadRelationships' , do (results )
346+ if ! results || results .length == 0
347+ return results
348+
349+ for relationship in this ._relationships
350+ let relationshipName = relationship .relatedTable
351+ if relationship .type == ' belongsTo' || relationship .type == ' hasOne'
352+ relationshipName = singularize (relationship .relatedTable )
353+
354+ if relationship .type == ' belongsTo'
355+ await this ._loadBelongsTo (results , relationship , relationshipName )
356+ else if relationship .type == ' hasOne'
357+ await this ._loadHasOne (results , relationship , relationshipName )
358+ else if relationship .type == ' hasMany'
359+ await this ._loadHasMany (results , relationship , relationshipName )
360+ else if relationship .type == ' belongsToMany'
361+ await this ._loadBelongsToMany (results , relationship , relationshipName )
362+
363+ results
364+
365+ knex .QueryBuilder .extend ' _loadBelongsTo' , do (results , relationship , relationshipName )
366+ const hasForeignKey = results .length > 0 && results [0 ].hasOwnProperty (relationship .foreignKey )
367+
368+ if ! hasForeignKey
369+ const ids = results .map (do (result ) result .id ).filter (do (id ) id != null )
370+
371+ if ids .length == 0
372+ for result in results
373+ result [relationshipName ] = null
374+ return
375+
376+ const foreignKeyResults = await Database (this ._single .table )
377+ .select (' id' , relationship .foreignKey )
378+ .whereIn (' id' , ids )
379+
380+ const foreignKeyMap = {}
381+ for row in foreignKeyResults
382+ foreignKeyMap [row .id ] = row [relationship .foreignKey ]
383+
384+ for result in results
385+ result [relationship .foreignKey ] = foreignKeyMap [result .id ]
386+
387+ const foreignKeys = results .map (do (result ) result [relationship .foreignKey ]).filter (do (key ) key != null )
388+
389+ if foreignKeys .length == 0
390+ for result in results
391+ result [relationshipName ] = null
392+ return
393+
394+ let relatedQuery = Database (relationship .relatedTable ).whereIn (relationship .localKey , foreignKeys )
395+
396+ if relationship .queryCallback
397+ relatedQuery = relationship .queryCallback (relatedQuery )
398+
399+ const relatedResults = await relatedQuery
400+ const relatedMap = {}
401+
402+ for related in relatedResults
403+ relatedMap [related [relationship .localKey ]] = related
404+
405+ for result in results
406+ result [relationshipName ] = relatedMap [result [relationship .foreignKey ]] || null
407+
408+ knex .QueryBuilder .extend ' _loadHasOne' , do (results , relationship , relationshipName )
409+ const localKeys = results .map (do (result ) result [relationship .localKey ]).filter (do (key ) key != null )
410+
411+ if localKeys .length == 0
412+ for result in results
413+ result [relationshipName ] = null
414+ return
415+
416+ let relatedQuery = Database (relationship .relatedTable ).whereIn (relationship .foreignKey , localKeys )
417+
418+ if relationship .queryCallback
419+ relatedQuery = relationship .queryCallback (relatedQuery )
420+
421+ const relatedResults = await relatedQuery
422+ const relatedMap = {}
423+
424+ for related in relatedResults
425+ relatedMap [related [relationship .foreignKey ]] = related
426+
427+ for result in results
428+ result [relationshipName ] = relatedMap [result [relationship .localKey ]] || null
429+
430+ knex .QueryBuilder .extend ' _loadHasMany' , do (results , relationship , relationshipName )
431+ const localKeys = results .map (do (result ) result [relationship .localKey ]).filter (do (key ) key != null )
432+
433+ if localKeys .length == 0
434+ for result in results
435+ result [relationshipName ] = []
436+ return
437+
438+ let relatedQuery = Database (relationship .relatedTable ).whereIn (relationship .foreignKey , localKeys )
439+
440+ if relationship .queryCallback
441+ relatedQuery = relationship .queryCallback (relatedQuery )
442+
443+ const relatedResults = await relatedQuery
444+ const relatedMap = {}
445+
446+ for related in relatedResults
447+ const key = related [relationship .foreignKey ]
448+ if ! relatedMap [key ]
449+ relatedMap [key ] = []
450+ relatedMap [key ].push (related )
451+
452+ for result in results
453+ result [relationshipName ] = relatedMap [result [relationship .localKey ]] || []
454+
455+ knex .QueryBuilder .extend ' _loadBelongsToMany' , do (results , relationship , relationshipName )
456+ const localKeys = results .map (do (result ) result [relationship .localKey ]).filter (do (key ) key != null )
457+
458+ if localKeys .length == 0
459+ for result in results
460+ result [relationshipName ] = []
461+ return
462+
463+ const pivotQuery = Database (relationship .pivotTable )
464+ .whereIn (relationship .foreignKey , localKeys )
465+ .select (relationship .foreignKey , relationship .relatedKey )
466+
467+ const pivotResults = await pivotQuery
468+ const pivotMap = {}
469+
470+ for pivot in pivotResults
471+ const key = pivot [relationship .foreignKey ]
472+ if ! pivotMap [key ]
473+ pivotMap [key ] = []
474+ pivotMap [key ].push (pivot [relationship .relatedKey ])
475+
476+ const allRelatedIds = [... new Set (pivotResults .map (do (pivot ) pivot [relationship .relatedKey ]))]
477+
478+ if allRelatedIds .length == 0
479+ for result in results
480+ result [relationshipName ] = []
481+ return
482+
483+ let relatedQuery = Database (relationship .relatedTable ).whereIn (relationship .relatedLocalKey , allRelatedIds )
484+
485+ if relationship .queryCallback
486+ relatedQuery = relationship .queryCallback (relatedQuery )
487+
488+ const relatedResults = await relatedQuery
489+ const relatedMap = {}
490+
491+ for related in relatedResults
492+ relatedMap [related [relationship .relatedLocalKey ]] = related
493+
494+ for result in results
495+ const relatedIds = pivotMap [result [relationship .localKey ]] || []
496+ result [relationshipName ] = relatedIds .map (do (id ) relatedMap [id ]).filter (do (item ) item != null )
497+
193498 knex .QueryBuilder .extend ' softDelete' , do this .update ({ deleted_at: Database .fn .now! })
194499
195500 knex .QueryBuilder .extend ' restore' , do this .update ({ deleted_at: null })
0 commit comments