@reldens/storage 0.117.0 → 0.119.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.claude/test-architecture.md +27 -17
  2. package/CLAUDE.md +67 -11
  3. package/README.md +204 -22
  4. package/bin/reldens-storage.js +43 -10
  5. package/index.js +53 -12
  6. package/lib/drizzle/drizzle-data-server.js +123 -0
  7. package/lib/drizzle/drizzle-driver.js +228 -0
  8. package/lib/drizzle/drizzle-models-generation.js +49 -0
  9. package/lib/drizzle/drizzle-modules-loader.js +32 -0
  10. package/lib/drizzle/drizzle-modules-validator.js +74 -0
  11. package/lib/entities-generator.js +36 -7
  12. package/lib/entity-templates/drizzle-model.template +28 -0
  13. package/lib/entity-templates/query-builder-model.template +17 -0
  14. package/lib/generators/base-generator.js +13 -0
  15. package/lib/generators/entities-generation.js +0 -13
  16. package/lib/generators/models-generation.js +179 -680
  17. package/lib/generators/relations-detection.js +171 -0
  18. package/lib/knex/knex-data-server.js +128 -0
  19. package/lib/knex/knex-driver.js +157 -0
  20. package/lib/knex/knex-modules-loader.js +28 -0
  21. package/lib/knex/knex-modules-validator.js +45 -0
  22. package/lib/kysely/kysely-data-server.js +128 -0
  23. package/lib/kysely/kysely-driver.js +176 -0
  24. package/lib/kysely/kysely-modules-loader.js +28 -0
  25. package/lib/kysely/kysely-modules-validator.js +53 -0
  26. package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
  27. package/lib/mikro-orm/mikro-orm-driver.js +317 -108
  28. package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
  29. package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
  30. package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
  31. package/lib/mysql2-connection-config.js +38 -0
  32. package/lib/objection-js/objection-js-data-server.js +71 -125
  33. package/lib/objection-js/objection-js-driver.js +4 -1
  34. package/lib/objection-js/objection-js-models-generation.js +98 -0
  35. package/lib/objection-js/objection-modules-loader.js +28 -0
  36. package/lib/objection-js/objection-modules-validator.js +31 -0
  37. package/lib/package-resolver.js +45 -0
  38. package/lib/prisma/prisma-client-loader.js +11 -1
  39. package/lib/prisma/prisma-driver.js +16 -2
  40. package/lib/prisma/prisma-filter-processor.js +5 -6
  41. package/lib/prisma/prisma-models-generation.js +236 -0
  42. package/lib/prisma/prisma-schema-generator.js +5 -1
  43. package/lib/prisma/prisma-type-caster.js +14 -0
  44. package/lib/query-builder-driver.js +210 -0
  45. package/lib/query-builder-models-generation.js +80 -0
  46. package/lib/relations-loader.js +192 -0
  47. package/lib/type-mapper.js +38 -0
  48. package/package.json +8 -8
  49. package/tests/.env.test.example +6 -0
  50. package/tests/fixtures/categories-fixtures.js +8 -0
  51. package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +10 -1
  52. package/tests/fixtures/expected-entities/entities-translations.js +2 -0
  53. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
  54. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +30 -10
  55. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +15 -14
  56. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
  57. package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +3 -1
  58. package/tests/fixtures/product-details-fixtures.js +4 -1
  59. package/tests/fixtures/reviews-fixtures.js +2 -1
  60. package/tests/fixtures/sql/test-schema.sql +11 -2
  61. package/tests/fixtures/table-columns-fixtures.js +97 -0
  62. package/tests/integration/test-cross-driver-equivalence.js +286 -0
  63. package/tests/integration/test-nested-filters.js +265 -408
  64. package/tests/integration/test-relations.js +135 -0
  65. package/tests/integration/test-reldens-sample-data.js +255 -0
  66. package/tests/integration/test-reldens-shape-relations.js +292 -0
  67. package/tests/run-tests.js +111 -65
  68. package/tests/unit/test-drivers.js +7 -1
  69. package/tests/unit/test-models-generation.js +28 -18
  70. package/tests/utils/column-value-assert.js +139 -0
  71. package/tests/utils/entity-projection.js +111 -0
  72. package/tests/utils/observed-value.js +41 -0
  73. package/tests/utils/projection-difference.js +151 -0
  74. package/tests/utils/relations-shape-support.js +104 -0
  75. package/tests/utils/reldens-schema-loader.js +274 -0
  76. package/tests/utils/test-helpers.js +143 -13
  77. package/tests/utils/test-runner.js +26 -5
  78. package/tests/utils/whole-result-assert.js +243 -0
@@ -6,7 +6,6 @@
6
6
 
7
7
  const { BaseDriver } = require('../base-driver');
8
8
  const { Logger, sc } = require('@reldens/utils');
9
- const { Collection } = require('@mikro-orm/core');
10
9
 
11
10
  class MikroOrmDriver extends BaseDriver
12
11
  {
@@ -26,6 +25,12 @@ class MikroOrmDriver extends BaseDriver
26
25
  Logger.critical('Missing raw entity on Mikro ORM driver.');
27
26
  return false;
28
27
  }
28
+ this.mikroOrmModules = sc.get(props, 'mikroOrmModules', false);
29
+ if(!this.mikroOrmModules){
30
+ Logger.critical('Missing Mikro ORM modules on Mikro ORM driver.');
31
+ return false;
32
+ }
33
+ this.Collection = this.mikroOrmModules.Collection;
29
34
  this.orm = props.orm;
30
35
  this.server = props.server;
31
36
  this.entitySchema = (sc.isObject(this.rawModel) && sc.hasOwn(this.rawModel, 'schema')) ? this.rawModel.schema : this.rawModel;
@@ -134,21 +139,51 @@ class MikroOrmDriver extends BaseDriver
134
139
  return prop.joinColumns[0] || false;
135
140
  }
136
141
 
137
- async create(params)
142
+ removeRelationsFromParams(params)
138
143
  {
139
- let transformed = this.transformFkToRelations(params);
140
- let newInstance = await this.orm.em.create(this.entitySchema, transformed);
141
- await this.orm.em.upsert(newInstance);
142
- await this.orm.em.flush();
144
+ if(!params || 'object' !== typeof params){
145
+ return params;
146
+ }
147
+ let columnsOnly = {...params};
148
+ for(let relationName of this.getAllRelations()){
149
+ delete columnsOnly[relationName];
150
+ }
151
+ return columnsOnly;
152
+ }
153
+
154
+ async createEntity(params)
155
+ {
156
+ let writeManager = this.orm.em.fork();
157
+ let newInstance = await writeManager.create(this.entitySchema, this.removeRelationsFromParams(params));
158
+ await writeManager.upsert(newInstance);
159
+ await writeManager.flush();
143
160
  return newInstance;
144
161
  }
145
162
 
146
- async createWithRelations(params, relations)
163
+ async create(params)
147
164
  {
148
- if(!sc.isArray(relations) || 0 === relations.length){
149
- relations = this.getAllRelations();
165
+ return this.withoutUninitializedCollections(await this.createEntity(params));
166
+ }
167
+
168
+ withoutUninitializedCollections(entity)
169
+ {
170
+ if(!entity || !this.Collection){
171
+ return entity;
172
+ }
173
+ let result = {};
174
+ for(let key of Object.keys(entity)){
175
+ if(entity[key] instanceof this.Collection && !entity[key].isInitialized()){
176
+ continue;
177
+ }
178
+ result[key] = entity[key];
150
179
  }
151
- let newInstance = await this.create(params);
180
+ return result;
181
+ }
182
+
183
+ async createWithRelations(params, relations)
184
+ {
185
+ relations = this.normalizeRelations(relations);
186
+ let newInstance = await this.createEntity(params);
152
187
  await this.createNested(newInstance, params, relations);
153
188
  return await this.appendRelationsToCollection(newInstance, relations);
154
189
  }
@@ -159,9 +194,9 @@ class MikroOrmDriver extends BaseDriver
159
194
  if(0 === entities.length){
160
195
  return false;
161
196
  }
162
- let transformed = this.transformFkToRelations(updatePatch);
197
+ let columnsOnly = this.removeRelationsFromParams(updatePatch);
163
198
  for(let entity of entities){
164
- Object.assign(entity, transformed);
199
+ Object.assign(entity, columnsOnly);
165
200
  await this.orm.em.upsert(entity);
166
201
  await this.orm.em.flush();
167
202
  }
@@ -190,7 +225,7 @@ class MikroOrmDriver extends BaseDriver
190
225
  if(1 < entities.length){
191
226
  Logger.warning('Multiple entities updated by ID: '+entities.map(e => e.id).join(', '));
192
227
  }
193
- return entities.shift();
228
+ return this.withoutUninitializedCollections(entities.shift());
194
229
  }
195
230
 
196
231
  async upsert(params, filters)
@@ -206,8 +241,7 @@ class MikroOrmDriver extends BaseDriver
206
241
  if(filters){
207
242
  let existent = await this.loadOne(filters);
208
243
  if(existent){
209
- let transformed = this.transformFkToRelations(params);
210
- return this.updateById(existent.id, transformed);
244
+ return this.updateById(existent.id, params);
211
245
  }
212
246
  }
213
247
  return this.create(params);
@@ -224,18 +258,18 @@ class MikroOrmDriver extends BaseDriver
224
258
  await this.orm.em.remove(entry);
225
259
  }
226
260
  await this.orm.em.flush();
227
- return true;
261
+ return entries.length;
228
262
  }
229
263
 
230
264
  async deleteById(id)
231
265
  {
232
- let entity = await this.loadById(id);
266
+ let entity = await this.repository.findOne(this.processFilters({id}));
233
267
  if(!entity){
234
268
  return false;
235
269
  }
236
270
  await this.orm.em.remove(entity);
237
271
  await this.orm.em.flush();
238
- return true;
272
+ return 1;
239
273
  }
240
274
 
241
275
  async count(filters)
@@ -246,9 +280,7 @@ class MikroOrmDriver extends BaseDriver
246
280
 
247
281
  async countWithRelations(filters, relations)
248
282
  {
249
- if(!sc.isArray(relations) || 0 === relations.length){
250
- relations = this.getAllRelations();
251
- }
283
+ relations = this.normalizeRelations(relations);
252
284
  let processedFilters = this.processFilters(filters);
253
285
  if(0 === relations.length || !this.entitySchema.meta || !this.entitySchema.meta.properties){
254
286
  return this.repository.count(processedFilters);
@@ -261,7 +293,7 @@ class MikroOrmDriver extends BaseDriver
261
293
  if(!prop || !prop.kind || (!this.isOwningRelation(prop) && !this.isInverseRelation(prop))){
262
294
  continue;
263
295
  }
264
- let relationDriver = this.server.entityManager.get(prop.entity);
296
+ let relationDriver = this.relationDriverFor(prop);
265
297
  if(!relationDriver || !relationDriver.entitySchema){
266
298
  continue;
267
299
  }
@@ -281,106 +313,180 @@ class MikroOrmDriver extends BaseDriver
281
313
  return queryBuilder.getCount();
282
314
  }
283
315
 
284
- loadAll()
316
+ async loadAll()
285
317
  {
286
- return this.repository.findAll();
318
+ return this.toPlainResult(await this.repository.findAll());
287
319
  }
288
320
 
289
- async loadAllWithRelations(relations)
321
+ normalizeRelations(relations)
290
322
  {
323
+ if(sc.isString(relations)){
324
+ relations = this.parseRelationsString(relations);
325
+ }
291
326
  if(!sc.isArray(relations) || 0 === relations.length){
292
- relations = this.getAllRelations();
327
+ return this.getAllRelations();
293
328
  }
294
- let entities = await this.loadAll();
295
- return await this.appendRelationsToCollection(entities, relations);
329
+ return relations;
296
330
  }
297
331
 
298
- load(filters)
332
+ toPlainResult(result, populatedPaths, ancestors)
299
333
  {
300
- let processedFilters = this.processFilters(filters);
301
- return this.repository.find(processedFilters, this.queryBuilder(true, true, true));
334
+ if(!result || 'object' !== typeof result || result instanceof Date){
335
+ return result;
336
+ }
337
+ if(!ancestors){
338
+ ancestors = new Set();
339
+ }
340
+ if(!populatedPaths){
341
+ populatedPaths = {};
342
+ }
343
+ if(result instanceof this.Collection){
344
+ if(!result.isInitialized()){
345
+ return [];
346
+ }
347
+ return this.toPlainList(result.getItems(), populatedPaths, ancestors);
348
+ }
349
+ if(sc.isArray(result)){
350
+ return this.toPlainList(result, populatedPaths, ancestors);
351
+ }
352
+ return this.toPlainEntity(result, populatedPaths, ancestors);
302
353
  }
303
354
 
304
- async loadWithRelations(filters, relations)
355
+ toPlainList(result, populatedPaths, ancestors)
305
356
  {
306
- if(!sc.isArray(relations) || 0 === relations.length){
307
- relations = this.getAllRelations();
357
+ ancestors.add(result);
358
+ let plainList = [];
359
+ for(let item of result){
360
+ if(ancestors.has(item)){
361
+ continue;
362
+ }
363
+ plainList.push(this.toPlainResult(item, populatedPaths, ancestors));
308
364
  }
309
- let processedFilters = this.processFilters(filters);
310
- let entitiesCollection = await this.repository.find(processedFilters, this.queryBuilder(true, true, true));
311
- return await this.appendRelationsToCollection(entitiesCollection, relations);
365
+ ancestors.delete(result);
366
+ return plainList;
312
367
  }
313
368
 
314
- loadBy(field, fieldValue, operator = null)
369
+ toPlainEntity(result, populatedPaths, ancestors)
315
370
  {
316
- let filter = this.createSingleFilter(field, fieldValue, operator);
317
- let processedFilters = this.processFilters(filter);
318
- return this.repository.find(processedFilters, this.queryBuilder(true, true, true));
371
+ ancestors.add(result);
372
+ let relationKeys = this.getAllRelations();
373
+ let plainEntity = {};
374
+ for(let key of Object.keys(result)){
375
+ if(ancestors.has(result[key])){
376
+ continue;
377
+ }
378
+ if(-1 === relationKeys.indexOf(key)){
379
+ plainEntity[key] = result[key];
380
+ continue;
381
+ }
382
+ if(!populatedPaths[key]){
383
+ continue;
384
+ }
385
+ plainEntity[key] = this.toPlainRelation(key, result[key], populatedPaths[key], ancestors);
386
+ }
387
+ ancestors.delete(result);
388
+ return plainEntity;
319
389
  }
320
390
 
321
- async loadByWithRelations(field, fieldValue, relations, operator = null)
391
+ toPlainRelation(propName, relationValue, populatedPaths, ancestors)
322
392
  {
323
- if(!sc.isArray(relations) || 0 === relations.length){
324
- relations = this.getAllRelations();
393
+ let relationDriver = this.relationDriverFor(this.entitySchema.meta.properties[propName]);
394
+ if(!relationDriver){
395
+ return this.toPlainResult(relationValue, populatedPaths, ancestors);
325
396
  }
397
+ return relationDriver.toPlainResult(relationValue, populatedPaths, ancestors);
398
+ }
399
+
400
+ async plainRelationsResult(entitiesCollection, relations)
401
+ {
402
+ return this.toPlainResult(
403
+ await this.appendRelationsToCollection(entitiesCollection, relations),
404
+ this.populatedPathsTree(relations)
405
+ );
406
+ }
407
+
408
+ findEntities(filters)
409
+ {
410
+ return this.repository.find(this.processFilters(filters), this.queryBuilder(true, true, true));
411
+ }
412
+
413
+ findOneEntity(filters)
414
+ {
415
+ return this.repository.findOne(this.processFilters(filters), this.queryBuilder(false, true, true));
416
+ }
417
+
418
+ async loadAllWithRelations(relations)
419
+ {
420
+ relations = this.normalizeRelations(relations);
421
+ let entities = await this.repository.findAll();
422
+ return this.plainRelationsResult(entities, relations);
423
+ }
424
+
425
+ async load(filters)
426
+ {
427
+ return this.toPlainResult(await this.findEntities(filters));
428
+ }
429
+
430
+ async loadWithRelations(filters, relations)
431
+ {
432
+ relations = this.normalizeRelations(relations);
433
+ let entitiesCollection = await this.findEntities(filters);
434
+ return this.plainRelationsResult(entitiesCollection, relations);
435
+ }
436
+
437
+ async loadBy(field, fieldValue, operator = null)
438
+ {
439
+ return this.toPlainResult(await this.findEntities(this.createSingleFilter(field, fieldValue, operator)));
440
+ }
441
+
442
+ async loadByWithRelations(field, fieldValue, relations, operator = null)
443
+ {
444
+ relations = this.normalizeRelations(relations);
326
445
  let filter = this.createSingleFilter(field, fieldValue, operator);
327
- let processedFilters = this.processFilters(filter);
328
- let entitiesCollection = await this.repository.find(processedFilters, this.queryBuilder(true, true, true));
329
- return await this.appendRelationsToCollection(entitiesCollection, relations);
446
+ let entitiesCollection = await this.findEntities(filter);
447
+ return this.plainRelationsResult(entitiesCollection, relations);
330
448
  }
331
449
 
332
- loadById(id)
450
+ async loadById(id)
333
451
  {
334
- return this.loadOneBy('id', id);
452
+ return this.toPlainResult(await this.findOneEntity({id}));
335
453
  }
336
454
 
337
455
  async loadByIdWithRelations(id, relations)
338
456
  {
339
- if(!sc.isArray(relations) || 0 === relations.length){
340
- relations = this.getAllRelations();
341
- }
342
- let entity = await this.loadOneBy('id', id);
343
- return await this.appendRelationsToCollection(entity, relations);
457
+ relations = this.normalizeRelations(relations);
458
+ let entity = await this.findOneEntity({id});
459
+ return this.plainRelationsResult(entity, relations);
344
460
  }
345
461
 
346
- loadByIds(ids)
462
+ async loadByIds(ids)
347
463
  {
348
- let processedFilters = this.processFilters({id: {$in: ids}});
349
- return this.repository.find(processedFilters);
464
+ return this.toPlainResult(await this.repository.find(this.processFilters({id: {$in: ids}})));
350
465
  }
351
466
 
352
- loadOne(filters)
467
+ async loadOne(filters)
353
468
  {
354
- let processedFilters = this.processFilters(filters);
355
- return this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
469
+ return this.toPlainResult(await this.findOneEntity(filters));
356
470
  }
357
471
 
358
472
  async loadOneWithRelations(filters, relations)
359
473
  {
360
- if(!sc.isArray(relations) || 0 === relations.length){
361
- relations = this.getAllRelations();
362
- }
363
- let processedFilters = this.processFilters(filters);
364
- let entitiesCollection = await this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
365
- return this.appendRelationsToCollection(entitiesCollection, relations);
474
+ relations = this.normalizeRelations(relations);
475
+ let entitiesCollection = await this.findOneEntity(filters);
476
+ return this.plainRelationsResult(entitiesCollection, relations);
366
477
  }
367
478
 
368
- loadOneBy(field, fieldValue, operator = null)
479
+ async loadOneBy(field, fieldValue, operator = null)
369
480
  {
370
- let filter = this.createSingleFilter(field, fieldValue, operator);
371
- let processedFilters = this.processFilters(filter);
372
- return this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
481
+ return this.toPlainResult(await this.findOneEntity(this.createSingleFilter(field, fieldValue, operator)));
373
482
  }
374
483
 
375
484
  async loadOneByWithRelations(field, fieldValue, relations, operator = null)
376
485
  {
377
- if(!sc.isArray(relations) || 0 === relations.length){
378
- relations = this.getAllRelations();
379
- }
486
+ relations = this.normalizeRelations(relations);
380
487
  let filter = this.createSingleFilter(field, fieldValue, operator);
381
- let processedFilters = this.processFilters(filter);
382
- let entitiesCollection = await this.repository.findOne(processedFilters, this.queryBuilder(false, true, true));
383
- return this.appendRelationsToCollection(entitiesCollection, relations);
488
+ let entitiesCollection = await this.findOneEntity(filter);
489
+ return this.plainRelationsResult(entitiesCollection, relations);
384
490
  }
385
491
 
386
492
  processFilters(filters)
@@ -391,6 +497,9 @@ class MikroOrmDriver extends BaseDriver
391
497
  let processedFilters = {};
392
498
  for(let key of Object.keys(filters)){
393
499
  let value = filters[key];
500
+ if(('AND' === key || 'OR' === key) && sc.isArray(value) && 0 === value.length){
501
+ continue;
502
+ }
394
503
  if('AND' === key && sc.isArray(value)){
395
504
  processedFilters.$and = value.map(condition => this.processFilters(condition));
396
505
  continue;
@@ -467,15 +576,39 @@ class MikroOrmDriver extends BaseDriver
467
576
  return filter;
468
577
  }
469
578
 
470
- convertCollectionsToArrays(entity)
579
+ populatedPathsTree(relations)
580
+ {
581
+ let tree = {};
582
+ if(!sc.isArray(relations)){
583
+ return tree;
584
+ }
585
+ for(let relationPath of relations){
586
+ let node = tree;
587
+ for(let pathPart of String(relationPath).split('.')){
588
+ if(!node[pathPart]){
589
+ node[pathPart] = {};
590
+ }
591
+ node = node[pathPart];
592
+ }
593
+ }
594
+ return tree;
595
+ }
596
+
597
+ convertCollectionsToArrays(entity, populatedPaths)
471
598
  {
472
599
  if(!entity){
473
600
  return entity;
474
601
  }
475
- for(let key of Object.keys(entity)){
476
- if(entity[key] && entity[key] instanceof Collection && entity[key].isInitialized()){
477
- entity[key] = entity[key].getItems();
602
+ for(let key of Object.keys(populatedPaths)){
603
+ if(!entity[key] || !(entity[key] instanceof this.Collection)){
604
+ continue;
605
+ }
606
+ if(!entity[key].isInitialized()){
607
+ Logger.warning('Relation "'+key+'" was not populated on '+this.tableName()+', it will be empty.');
608
+ entity[key] = [];
609
+ continue;
478
610
  }
611
+ entity[key] = entity[key].getItems();
479
612
  }
480
613
  return entity;
481
614
  }
@@ -511,13 +644,104 @@ class MikroOrmDriver extends BaseDriver
511
644
  return entitiesCollection;
512
645
  }
513
646
  await this.orm.em.populate(entitiesCollection, relations);
647
+ let populatedPaths = this.populatedPathsTree(relations);
514
648
  if(sc.isArray(entitiesCollection)){
515
649
  for(let entity of entitiesCollection){
516
- this.convertCollectionsToArrays(entity);
650
+ this.restoreRelatedForeignKeys(entity, new Set(), populatedPaths);
651
+ await this.resolveKeyMappedRelations(entity, populatedPaths);
517
652
  }
518
653
  return entitiesCollection;
519
654
  }
520
- return this.convertCollectionsToArrays(entitiesCollection);
655
+ this.restoreRelatedForeignKeys(entitiesCollection, new Set(), populatedPaths);
656
+ await this.resolveKeyMappedRelations(entitiesCollection, populatedPaths);
657
+ return entitiesCollection;
658
+ }
659
+
660
+ async resolveKeyMappedRelations(entity, populatedPaths)
661
+ {
662
+ if(!entity || !this.entitySchema.meta || !this.entitySchema.meta.properties){
663
+ return entity;
664
+ }
665
+ for(let propName of Object.keys(populatedPaths)){
666
+ let prop = this.entitySchema.meta.properties[propName];
667
+ if(!prop || !prop.mapToPk || !prop.targetKey || sc.isObject(entity[propName])){
668
+ continue;
669
+ }
670
+ if(null === entity[propName] || 'undefined' === typeof entity[propName]){
671
+ continue;
672
+ }
673
+ let relationDriver = this.relationDriverFor(prop);
674
+ if(!relationDriver){
675
+ continue;
676
+ }
677
+ entity[propName] = await relationDriver.loadOneBy(prop.targetKey, entity[propName]);
678
+ }
679
+ return entity;
680
+ }
681
+
682
+ restoreRelatedForeignKeys(entity, visited, populatedPaths)
683
+ {
684
+ if(!entity || visited.has(entity)){
685
+ return;
686
+ }
687
+ visited.add(entity);
688
+ this.convertCollectionsToArrays(entity, populatedPaths);
689
+ this.addFkFieldsFromRelations(entity);
690
+ if(!this.entitySchema.meta || !this.entitySchema.meta.properties){
691
+ return;
692
+ }
693
+ for(let propName of Object.keys(populatedPaths)){
694
+ let relationDriver = this.relationDriverFor(this.entitySchema.meta.properties[propName]);
695
+ if(!relationDriver){
696
+ continue;
697
+ }
698
+ relationDriver.restoreRelatedEntities(entity[propName], visited, populatedPaths[propName]);
699
+ }
700
+ }
701
+
702
+ restoreRelatedEntities(relatedValue, visited, populatedPaths)
703
+ {
704
+ if(!relatedValue || relatedValue instanceof this.Collection){
705
+ return;
706
+ }
707
+ if(sc.isArray(relatedValue)){
708
+ for(let relatedEntity of relatedValue){
709
+ this.restoreRelatedForeignKeys(relatedEntity, visited, populatedPaths);
710
+ }
711
+ return;
712
+ }
713
+ this.restoreRelatedForeignKeys(relatedValue, visited, populatedPaths);
714
+ }
715
+
716
+ relationTargetClass(prop)
717
+ {
718
+ if(!sc.isFunction(prop?.entity)){
719
+ return false;
720
+ }
721
+ return prop.entity();
722
+ }
723
+
724
+ relationDriverFor(prop)
725
+ {
726
+ let targetClass = this.relationTargetClass(prop);
727
+ let targetClassName = (prop && prop.targetMeta) ? prop.targetMeta.className : false;
728
+ if(!targetClass && !targetClassName){
729
+ return false;
730
+ }
731
+ let entities = this.server.entityManager.entities;
732
+ for(let entityKey of Object.keys(entities)){
733
+ let candidate = entities[entityKey];
734
+ if(!candidate || !candidate.entitySchema || !candidate.entitySchema.meta){
735
+ continue;
736
+ }
737
+ if(targetClass && candidate.entitySchema.meta.class === targetClass){
738
+ return candidate;
739
+ }
740
+ if(targetClassName && candidate.entitySchema.meta.className === targetClassName){
741
+ return candidate;
742
+ }
743
+ }
744
+ return false;
521
745
  }
522
746
 
523
747
  async createNested(newInstance, params, relations)
@@ -534,12 +758,12 @@ class MikroOrmDriver extends BaseDriver
534
758
  if(sc.isArray(relations) && 0 < relations.length && -1 === relations.indexOf(relationName)){
535
759
  continue;
536
760
  }
537
- let relationDriver = this.server.entityManager.get(prop.entity);
538
- if(!relationDriver){
539
- Logger.warning('Entity not found for relation:', prop.entity);
761
+ if(!sc.hasOwn(params, relationName)){
540
762
  continue;
541
763
  }
542
- if(!sc.hasOwn(params, relationName)){
764
+ let relationDriver = this.relationDriverFor(prop);
765
+ if(!relationDriver){
766
+ Logger.warning('Entity not found for relation: '+relationName);
543
767
  continue;
544
768
  }
545
769
  if(this.isOwningRelation(prop)){
@@ -558,12 +782,12 @@ class MikroOrmDriver extends BaseDriver
558
782
 
559
783
  async createOne(params, relationName, relationDriver, newInstance, prop)
560
784
  {
561
- let nestedData = params[relationName];
785
+ let nestedData = Object.assign({}, params[relationName]);
562
786
  let fkColumn = this.nestedFkColumn(prop, relationDriver);
563
787
  if(fkColumn){
564
788
  nestedData[fkColumn] = newInstance.id;
565
789
  }
566
- let nestedObject = await relationDriver.create(nestedData);
790
+ let nestedObject = await relationDriver.createEntity(nestedData);
567
791
  await this.orm.em.flush();
568
792
  newInstance[relationName] = nestedObject;
569
793
  }
@@ -571,34 +795,19 @@ class MikroOrmDriver extends BaseDriver
571
795
  async createMany(params, relationName, relationDriver, newInstance, prop)
572
796
  {
573
797
  let nestedArray = [];
798
+ let fkColumn = this.nestedFkColumn(prop, relationDriver);
574
799
  for(let objectData of params[relationName]){
575
- if(prop.joinColumns && prop.joinColumns[0]){
576
- objectData[prop.joinColumns[0]] = newInstance.id;
800
+ let nestedData = Object.assign({}, objectData);
801
+ if(fkColumn){
802
+ nestedData[fkColumn] = newInstance.id;
577
803
  }
578
- let nestedObject = await relationDriver.create(objectData);
804
+ let nestedObject = await relationDriver.createEntity(nestedData);
579
805
  await this.orm.em.flush();
580
806
  nestedArray.push(nestedObject);
581
807
  }
582
808
  newInstance[relationName] = nestedArray;
583
809
  }
584
810
 
585
- transformFkToRelations(params)
586
- {
587
- if(!params || 'object' !== typeof params){
588
- return params;
589
- }
590
- let transformed = {...params};
591
- for(let fkColumn of Object.keys(this.fkMappings)){
592
- if(!sc.hasOwn(params, fkColumn) || null === params[fkColumn]){
593
- continue;
594
- }
595
- let mapping = this.fkMappings[fkColumn];
596
- transformed[mapping.relationKey] = this.orm.em.getReference(mapping.entityName, params[fkColumn]);
597
- delete transformed[fkColumn];
598
- }
599
- return transformed;
600
- }
601
-
602
811
  }
603
812
 
604
813
  module.exports.MikroOrmDriver = MikroOrmDriver;