@mikro-orm/core 7.0.2 → 7.0.3-dev.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 (205) hide show
  1. package/EntityManager.d.ts +583 -883
  2. package/EntityManager.js +1869 -1897
  3. package/MikroORM.d.ts +74 -103
  4. package/MikroORM.js +179 -178
  5. package/cache/CacheAdapter.d.ts +36 -36
  6. package/cache/FileCacheAdapter.d.ts +24 -30
  7. package/cache/FileCacheAdapter.js +78 -80
  8. package/cache/GeneratedCacheAdapter.d.ts +20 -18
  9. package/cache/GeneratedCacheAdapter.js +30 -30
  10. package/cache/MemoryCacheAdapter.d.ts +20 -18
  11. package/cache/MemoryCacheAdapter.js +36 -35
  12. package/cache/NullCacheAdapter.d.ts +16 -16
  13. package/cache/NullCacheAdapter.js +24 -24
  14. package/connections/Connection.d.ts +84 -95
  15. package/connections/Connection.js +168 -165
  16. package/drivers/DatabaseDriver.d.ts +80 -186
  17. package/drivers/DatabaseDriver.js +443 -450
  18. package/drivers/IDatabaseDriver.d.ts +301 -440
  19. package/entity/BaseEntity.d.ts +83 -120
  20. package/entity/BaseEntity.js +43 -43
  21. package/entity/Collection.d.ts +179 -212
  22. package/entity/Collection.js +721 -727
  23. package/entity/EntityAssigner.d.ts +77 -88
  24. package/entity/EntityAssigner.js +230 -231
  25. package/entity/EntityFactory.d.ts +54 -66
  26. package/entity/EntityFactory.js +383 -425
  27. package/entity/EntityHelper.d.ts +22 -34
  28. package/entity/EntityHelper.js +267 -280
  29. package/entity/EntityIdentifier.d.ts +4 -4
  30. package/entity/EntityIdentifier.js +10 -10
  31. package/entity/EntityLoader.d.ts +73 -103
  32. package/entity/EntityLoader.js +723 -753
  33. package/entity/EntityRepository.d.ts +201 -316
  34. package/entity/EntityRepository.js +213 -213
  35. package/entity/PolymorphicRef.d.ts +5 -5
  36. package/entity/PolymorphicRef.js +10 -10
  37. package/entity/Reference.d.ts +82 -126
  38. package/entity/Reference.js +274 -278
  39. package/entity/WrappedEntity.d.ts +72 -115
  40. package/entity/WrappedEntity.js +166 -168
  41. package/entity/defineEntity.d.ts +614 -1280
  42. package/entity/defineEntity.js +511 -520
  43. package/entity/utils.d.ts +3 -13
  44. package/entity/utils.js +73 -71
  45. package/entity/validators.js +43 -43
  46. package/entity/wrap.js +8 -8
  47. package/enums.d.ts +253 -258
  48. package/enums.js +252 -251
  49. package/errors.d.ts +72 -114
  50. package/errors.js +253 -350
  51. package/events/EventManager.d.ts +14 -26
  52. package/events/EventManager.js +77 -79
  53. package/events/EventSubscriber.d.ts +29 -29
  54. package/events/TransactionEventBroadcaster.d.ts +8 -15
  55. package/events/TransactionEventBroadcaster.js +14 -14
  56. package/exceptions.d.ts +40 -23
  57. package/exceptions.js +52 -35
  58. package/hydration/Hydrator.d.ts +17 -42
  59. package/hydration/Hydrator.js +43 -43
  60. package/hydration/ObjectHydrator.d.ts +17 -50
  61. package/hydration/ObjectHydrator.js +416 -479
  62. package/index.d.ts +2 -116
  63. package/index.js +1 -10
  64. package/logging/DefaultLogger.d.ts +32 -34
  65. package/logging/DefaultLogger.js +86 -86
  66. package/logging/Logger.d.ts +41 -41
  67. package/logging/SimpleLogger.d.ts +11 -13
  68. package/logging/SimpleLogger.js +22 -22
  69. package/logging/colors.d.ts +6 -6
  70. package/logging/colors.js +10 -11
  71. package/logging/inspect.js +7 -7
  72. package/metadata/EntitySchema.d.ts +127 -211
  73. package/metadata/EntitySchema.js +398 -397
  74. package/metadata/MetadataDiscovery.d.ts +114 -114
  75. package/metadata/MetadataDiscovery.js +1863 -1947
  76. package/metadata/MetadataProvider.d.ts +21 -24
  77. package/metadata/MetadataProvider.js +84 -82
  78. package/metadata/MetadataStorage.d.ts +32 -38
  79. package/metadata/MetadataStorage.js +118 -118
  80. package/metadata/MetadataValidator.d.ts +39 -39
  81. package/metadata/MetadataValidator.js +338 -381
  82. package/metadata/discover-entities.d.ts +2 -5
  83. package/metadata/discover-entities.js +27 -27
  84. package/metadata/types.d.ts +531 -615
  85. package/naming-strategy/AbstractNamingStrategy.d.ts +39 -54
  86. package/naming-strategy/AbstractNamingStrategy.js +85 -90
  87. package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
  88. package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
  89. package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
  90. package/naming-strategy/MongoNamingStrategy.js +18 -18
  91. package/naming-strategy/NamingStrategy.d.ts +99 -109
  92. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  93. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  94. package/not-supported.js +4 -7
  95. package/package.json +1 -1
  96. package/platforms/ExceptionConverter.d.ts +1 -1
  97. package/platforms/ExceptionConverter.js +4 -4
  98. package/platforms/Platform.d.ts +299 -308
  99. package/platforms/Platform.js +636 -659
  100. package/serialization/EntitySerializer.d.ts +26 -48
  101. package/serialization/EntitySerializer.js +218 -224
  102. package/serialization/EntityTransformer.d.ts +6 -10
  103. package/serialization/EntityTransformer.js +217 -219
  104. package/serialization/SerializationContext.d.ts +23 -27
  105. package/serialization/SerializationContext.js +105 -105
  106. package/types/ArrayType.d.ts +8 -8
  107. package/types/ArrayType.js +33 -33
  108. package/types/BigIntType.d.ts +10 -17
  109. package/types/BigIntType.js +37 -37
  110. package/types/BlobType.d.ts +3 -3
  111. package/types/BlobType.js +13 -13
  112. package/types/BooleanType.d.ts +4 -4
  113. package/types/BooleanType.js +12 -12
  114. package/types/CharacterType.d.ts +2 -2
  115. package/types/CharacterType.js +6 -6
  116. package/types/DateTimeType.d.ts +5 -5
  117. package/types/DateTimeType.js +15 -15
  118. package/types/DateType.d.ts +5 -5
  119. package/types/DateType.js +15 -15
  120. package/types/DecimalType.d.ts +7 -7
  121. package/types/DecimalType.js +26 -26
  122. package/types/DoubleType.d.ts +3 -3
  123. package/types/DoubleType.js +12 -12
  124. package/types/EnumArrayType.d.ts +5 -5
  125. package/types/EnumArrayType.js +24 -24
  126. package/types/EnumType.d.ts +3 -3
  127. package/types/EnumType.js +11 -11
  128. package/types/FloatType.d.ts +3 -3
  129. package/types/FloatType.js +9 -9
  130. package/types/IntegerType.d.ts +3 -3
  131. package/types/IntegerType.js +9 -9
  132. package/types/IntervalType.d.ts +4 -4
  133. package/types/IntervalType.js +12 -12
  134. package/types/JsonType.d.ts +8 -8
  135. package/types/JsonType.js +32 -32
  136. package/types/MediumIntType.d.ts +1 -1
  137. package/types/MediumIntType.js +3 -3
  138. package/types/SmallIntType.d.ts +3 -3
  139. package/types/SmallIntType.js +9 -9
  140. package/types/StringType.d.ts +4 -4
  141. package/types/StringType.js +12 -12
  142. package/types/TextType.d.ts +3 -3
  143. package/types/TextType.js +9 -9
  144. package/types/TimeType.d.ts +5 -5
  145. package/types/TimeType.js +17 -17
  146. package/types/TinyIntType.d.ts +3 -3
  147. package/types/TinyIntType.js +10 -10
  148. package/types/Type.d.ts +79 -83
  149. package/types/Type.js +82 -82
  150. package/types/Uint8ArrayType.d.ts +4 -4
  151. package/types/Uint8ArrayType.js +21 -21
  152. package/types/UnknownType.d.ts +4 -4
  153. package/types/UnknownType.js +12 -12
  154. package/types/UuidType.d.ts +5 -5
  155. package/types/UuidType.js +19 -19
  156. package/types/index.d.ts +49 -75
  157. package/types/index.js +26 -52
  158. package/typings.d.ts +729 -1211
  159. package/typings.js +231 -244
  160. package/unit-of-work/ChangeSet.d.ts +26 -26
  161. package/unit-of-work/ChangeSet.js +56 -56
  162. package/unit-of-work/ChangeSetComputer.d.ts +12 -12
  163. package/unit-of-work/ChangeSetComputer.js +170 -178
  164. package/unit-of-work/ChangeSetPersister.d.ts +44 -63
  165. package/unit-of-work/ChangeSetPersister.js +421 -442
  166. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  167. package/unit-of-work/CommitOrderCalculator.js +88 -89
  168. package/unit-of-work/IdentityMap.d.ts +31 -31
  169. package/unit-of-work/IdentityMap.js +105 -105
  170. package/unit-of-work/UnitOfWork.d.ts +141 -181
  171. package/unit-of-work/UnitOfWork.js +1183 -1200
  172. package/utils/AbstractMigrator.d.ts +91 -111
  173. package/utils/AbstractMigrator.js +275 -275
  174. package/utils/AbstractSchemaGenerator.d.ts +34 -43
  175. package/utils/AbstractSchemaGenerator.js +122 -121
  176. package/utils/AsyncContext.d.ts +3 -3
  177. package/utils/AsyncContext.js +35 -34
  178. package/utils/Configuration.d.ts +808 -852
  179. package/utils/Configuration.js +344 -359
  180. package/utils/Cursor.d.ts +22 -40
  181. package/utils/Cursor.js +127 -135
  182. package/utils/DataloaderUtils.d.ts +43 -58
  183. package/utils/DataloaderUtils.js +198 -203
  184. package/utils/EntityComparator.d.ts +81 -98
  185. package/utils/EntityComparator.js +728 -824
  186. package/utils/NullHighlighter.d.ts +1 -1
  187. package/utils/NullHighlighter.js +3 -3
  188. package/utils/QueryHelper.d.ts +51 -79
  189. package/utils/QueryHelper.js +361 -372
  190. package/utils/RawQueryFragment.d.ts +34 -50
  191. package/utils/RawQueryFragment.js +105 -107
  192. package/utils/RequestContext.d.ts +32 -32
  193. package/utils/RequestContext.js +53 -52
  194. package/utils/TransactionContext.d.ts +16 -16
  195. package/utils/TransactionContext.js +27 -27
  196. package/utils/TransactionManager.d.ts +58 -58
  197. package/utils/TransactionManager.js +197 -199
  198. package/utils/Utils.d.ts +145 -204
  199. package/utils/Utils.js +813 -814
  200. package/utils/clone.js +113 -104
  201. package/utils/env-vars.js +88 -90
  202. package/utils/fs-utils.d.ts +15 -15
  203. package/utils/fs-utils.js +181 -180
  204. package/utils/upsert-utils.d.ts +5 -20
  205. package/utils/upsert-utils.js +116 -114
@@ -8,451 +8,409 @@ import { JsonType } from '../types/JsonType.js';
8
8
  import { isRaw } from '../utils/RawQueryFragment.js';
9
9
  /** @internal Factory responsible for creating, merging, and hydrating entity instances. */
10
10
  export class EntityFactory {
11
- #driver;
12
- #platform;
13
- #config;
14
- #metadata;
15
- #hydrator;
16
- #eventManager;
17
- #comparator;
18
- #em;
19
- constructor(em) {
20
- this.#em = em;
21
- this.#driver = this.#em.getDriver();
22
- this.#platform = this.#driver.getPlatform();
23
- this.#config = this.#em.config;
24
- this.#metadata = this.#em.getMetadata();
25
- this.#hydrator = this.#config.getHydrator(this.#metadata);
26
- this.#eventManager = this.#em.getEventManager();
27
- this.#comparator = this.#em.getComparator();
28
- }
29
- /** Creates a new entity instance or returns an existing one from the identity map, hydrating it with the provided data. */
30
- create(entityName, data, options = {}) {
31
- data = Reference.unwrapReference(data);
32
- options.initialized ??= true;
33
- if (data.__entity) {
34
- return data;
11
+ #driver;
12
+ #platform;
13
+ #config;
14
+ #metadata;
15
+ #hydrator;
16
+ #eventManager;
17
+ #comparator;
18
+ #em;
19
+ constructor(em) {
20
+ this.#em = em;
21
+ this.#driver = this.#em.getDriver();
22
+ this.#platform = this.#driver.getPlatform();
23
+ this.#config = this.#em.config;
24
+ this.#metadata = this.#em.getMetadata();
25
+ this.#hydrator = this.#config.getHydrator(this.#metadata);
26
+ this.#eventManager = this.#em.getEventManager();
27
+ this.#comparator = this.#em.getComparator();
35
28
  }
36
- const meta = this.#metadata.get(entityName);
37
- if (meta.virtual) {
38
- data = { ...data };
39
- const entity = this.createEntity(data, meta, options);
40
- this.hydrate(entity, meta, data, options);
41
- return entity;
42
- }
43
- if (meta.serializedPrimaryKey) {
44
- this.denormalizePrimaryKey(meta, data);
45
- }
46
- const meta2 = this.processDiscriminatorColumn(meta, data);
47
- const exists = this.findEntity(data, meta2, options);
48
- let wrapped = exists && helper(exists);
49
- if (wrapped && !options.refresh) {
50
- wrapped.__processing = true;
51
- Utils.dropUndefinedProperties(data);
52
- this.mergeData(meta2, exists, data, options);
53
- wrapped.__processing = false;
54
- if (wrapped.isInitialized()) {
55
- return exists;
56
- }
57
- }
58
- data = { ...data };
59
- const entity = exists ?? this.createEntity(data, meta2, options);
60
- wrapped = helper(entity);
61
- wrapped.__processing = true;
62
- wrapped.__initialized = options.initialized;
63
- if (options.newEntity || meta.forceConstructor || meta.virtual) {
64
- const tmp = { ...data };
65
- meta.constructorParams?.forEach(prop => delete tmp[prop]);
66
- this.hydrate(entity, meta2, tmp, options);
67
- // since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
68
- // we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
69
- // even if they are not part of constructor parameters (as this is otherwise normalized during hydration, here only in `tmp`)
70
- if (options.convertCustomTypes) {
71
- for (const prop of meta.props) {
72
- if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
73
- if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
74
- continue;
29
+ /** Creates a new entity instance or returns an existing one from the identity map, hydrating it with the provided data. */
30
+ create(entityName, data, options = {}) {
31
+ data = Reference.unwrapReference(data);
32
+ options.initialized ??= true;
33
+ if (data.__entity) {
34
+ return data;
35
+ }
36
+ const meta = this.#metadata.get(entityName);
37
+ if (meta.virtual) {
38
+ data = { ...data };
39
+ const entity = this.createEntity(data, meta, options);
40
+ this.hydrate(entity, meta, data, options);
41
+ return entity;
42
+ }
43
+ if (meta.serializedPrimaryKey) {
44
+ this.denormalizePrimaryKey(meta, data);
45
+ }
46
+ const meta2 = this.processDiscriminatorColumn(meta, data);
47
+ const exists = this.findEntity(data, meta2, options);
48
+ let wrapped = exists && helper(exists);
49
+ if (wrapped && !options.refresh) {
50
+ wrapped.__processing = true;
51
+ Utils.dropUndefinedProperties(data);
52
+ this.mergeData(meta2, exists, data, options);
53
+ wrapped.__processing = false;
54
+ if (wrapped.isInitialized()) {
55
+ return exists;
75
56
  }
76
- if (
77
- [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
78
- Utils.isPlainObject(data[prop.name])
79
- ) {
80
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
57
+ }
58
+ data = { ...data };
59
+ const entity = exists ?? this.createEntity(data, meta2, options);
60
+ wrapped = helper(entity);
61
+ wrapped.__processing = true;
62
+ wrapped.__initialized = options.initialized;
63
+ if (options.newEntity || meta.forceConstructor || meta.virtual) {
64
+ const tmp = { ...data };
65
+ meta.constructorParams?.forEach(prop => delete tmp[prop]);
66
+ this.hydrate(entity, meta2, tmp, options);
67
+ // since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
68
+ // we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
69
+ // even if they are not part of constructor parameters (as this is otherwise normalized during hydration, here only in `tmp`)
70
+ if (options.convertCustomTypes) {
71
+ for (const prop of meta.props) {
72
+ if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
73
+ if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
74
+ continue;
75
+ }
76
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
77
+ Utils.isPlainObject(data[prop.name])) {
78
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
79
+ }
80
+ if (prop.customType instanceof JsonType && this.#platform.convertsJsonAutomatically()) {
81
+ data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.#platform, {
82
+ key: prop.name,
83
+ mode: 'hydration',
84
+ });
85
+ }
86
+ }
87
+ }
81
88
  }
82
- if (prop.customType instanceof JsonType && this.#platform.convertsJsonAutomatically()) {
83
- data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.#platform, {
84
- key: prop.name,
85
- mode: 'hydration',
86
- });
89
+ }
90
+ else {
91
+ this.hydrate(entity, meta2, data, options);
92
+ }
93
+ if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
94
+ Object.setPrototypeOf(entity, meta2.prototype);
95
+ }
96
+ if (options.merge && wrapped.hasPrimaryKey()) {
97
+ this.unitOfWork.register(entity, data, {
98
+ // Always refresh to ensure the payload is in correct shape for joined strategy. When loading nested relations,
99
+ // they will be created early without `Type.ensureComparable` being properly handled, resulting in extra updates.
100
+ refresh: options.initialized,
101
+ newEntity: options.newEntity,
102
+ loaded: options.initialized,
103
+ });
104
+ if (options.recomputeSnapshot) {
105
+ wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
87
106
  }
88
- }
89
107
  }
90
- }
91
- } else {
92
- this.hydrate(entity, meta2, data, options);
93
- }
94
- if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
95
- Object.setPrototypeOf(entity, meta2.prototype);
96
- }
97
- if (options.merge && wrapped.hasPrimaryKey()) {
98
- this.unitOfWork.register(entity, data, {
99
- // Always refresh to ensure the payload is in correct shape for joined strategy. When loading nested relations,
100
- // they will be created early without `Type.ensureComparable` being properly handled, resulting in extra updates.
101
- refresh: options.initialized,
102
- newEntity: options.newEntity,
103
- loaded: options.initialized,
104
- });
105
- if (options.recomputeSnapshot) {
106
- wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
107
- }
108
- }
109
- if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
110
- this.#eventManager.dispatchEvent(EventType.onInit, { entity, meta: meta2, em: this.#em });
111
- }
112
- wrapped.__processing = false;
113
- return entity;
114
- }
115
- /** Merges new data into an existing entity, preserving user-modified properties. */
116
- mergeData(meta, entity, data, options = {}) {
117
- // merge unchanged properties automatically
118
- data = QueryHelper.processParams(data);
119
- const existsData = this.#comparator.prepareEntity(entity);
120
- const originalEntityData = helper(entity).__originalEntityData ?? {};
121
- const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData);
122
- // version properties are not part of entity snapshots
123
- if (
124
- meta.versionProperty &&
125
- data[meta.versionProperty] &&
126
- data[meta.versionProperty] !== originalEntityData[meta.versionProperty]
127
- ) {
128
- diff[meta.versionProperty] = data[meta.versionProperty];
129
- }
130
- const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
131
- // do not override values changed by user
132
- Utils.keys(diff).forEach(key => delete diff2[key]);
133
- Utils.keys(diff2)
134
- .filter(key => {
135
- // ignore null values if there is already present non-null value
136
- if (existsData[key] != null) {
137
- return diff2[key] == null;
108
+ if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
109
+ this.#eventManager.dispatchEvent(EventType.onInit, { entity, meta: meta2, em: this.#em });
138
110
  }
139
- return diff2[key] === undefined;
140
- })
141
- .forEach(key => delete diff2[key]);
142
- // but always add collection properties and formulas if they are part of the `data`
143
- Utils.keys(data)
144
- .filter(
145
- key =>
146
- meta.properties[key]?.formula ||
147
- [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind),
148
- )
149
- .forEach(key => (diff2[key] = data[key]));
150
- // rehydrated with the new values, skip those changed by user
151
- // use full hydration if the entity is already initialized, even if the caller used `initialized: false`
152
- // (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
153
- const initialized = options.initialized || helper(entity).__initialized;
154
- this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
155
- // we need to update the entity data only with keys that were not present before
156
- const nullVal = this.#config.get('forceUndefined') ? undefined : null;
157
- Utils.keys(diff2).forEach(key => {
158
- const prop = meta.properties[key];
159
- if (
160
- [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
161
- Utils.isPlainObject(data[prop.name])
162
- ) {
163
- // oxfmt-ignore
164
- diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
165
- }
166
- if (
167
- !options.convertCustomTypes &&
168
- [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
169
- prop.customType?.ensureComparable(meta, prop) &&
170
- diff2[key] != null
171
- ) {
172
- const converted = prop.customType.convertToJSValue(diff2[key], this.#platform, { force: true });
173
- diff2[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { fromQuery: true });
174
- }
175
- originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
176
- helper(entity).__loadedProperties.add(key);
177
- });
178
- // in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
179
- meta.relations.forEach(prop => {
180
- if (
181
- [ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
182
- Array.isArray(data[prop.name])
183
- ) {
184
- // instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
185
- // we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
186
- data[prop.name]
187
- .filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
188
- .forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
189
- return;
190
- }
191
- if (
192
- [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
193
- Utils.isPlainObject(data[prop.name]) &&
194
- entity[prop.name] &&
195
- helper(entity[prop.name]).__initialized
196
- ) {
197
- this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
198
- }
199
- });
200
- this.unitOfWork.normalizeEntityData(meta, originalEntityData);
201
- }
202
- /** Creates or retrieves an uninitialized entity reference by its primary key or alternate key. */
203
- createReference(entityName, id, options = {}) {
204
- options.convertCustomTypes ??= true;
205
- const meta = this.#metadata.get(entityName);
206
- const schema = this.#driver.getSchemaName(meta, options);
207
- // Handle alternate key lookup
208
- if (options.key) {
209
- const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
210
- const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
211
- if (exists) {
212
- return exists;
213
- }
214
- // Create entity stub - storeByKey will set the alternate key property and store in identity map
215
- const entity = this.create(entityName, {}, { ...options, initialized: false });
216
- this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
217
- return entity;
218
- }
219
- if (meta.simplePK) {
220
- const exists = this.unitOfWork.getById(entityName, id, schema);
221
- if (exists) {
222
- return exists;
223
- }
224
- const data = Utils.isPlainObject(id) ? id : { [meta.primaryKeys[0]]: Array.isArray(id) ? id[0] : id };
225
- return this.create(entityName, data, { ...options, initialized: false });
226
- }
227
- if (Array.isArray(id)) {
228
- id = Utils.getPrimaryKeyCondFromArray(id, meta);
229
- }
230
- const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
231
- const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
232
- if (exists) {
233
- return exists;
234
- }
235
- if (Utils.isPrimaryKey(id)) {
236
- id = { [meta.primaryKeys[0]]: id };
237
- }
238
- return this.create(entityName, id, { ...options, initialized: false });
239
- }
240
- /** Creates an embeddable entity instance from the provided data. */
241
- createEmbeddable(entityName, data, options = {}) {
242
- data = { ...data };
243
- const meta = this.#metadata.get(entityName);
244
- const meta2 = this.processDiscriminatorColumn(meta, data);
245
- return this.createEntity(data, meta2, options);
246
- }
247
- /** Returns the EntityComparator instance used for diffing entities. */
248
- getComparator() {
249
- return this.#comparator;
250
- }
251
- createEntity(data, meta, options) {
252
- const schema = this.#driver.getSchemaName(meta, options);
253
- if (options.newEntity || meta.forceConstructor || meta.virtual) {
254
- if (meta.polymorphs) {
255
- throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
256
- }
257
- const params = this.extractConstructorParams(meta, data, options);
258
- const Entity = meta.class;
259
- // creates new instance via constructor as this is the new entity
260
- const entity = new Entity(...params);
261
- // creating managed entity instance when `forceEntityConstructor` is enabled,
262
- // we need to wipe all the values as they would cause update queries on next flush
263
- if (!options.newEntity && (meta.forceConstructor || this.#config.get('forceEntityConstructor'))) {
264
- meta.props
265
- .filter(prop => prop.persist !== false && !prop.primary && data[prop.name] === undefined)
266
- .forEach(prop => delete entity[prop.name]);
267
- }
268
- if (meta.virtual) {
111
+ wrapped.__processing = false;
269
112
  return entity;
270
- }
271
- helper(entity).__schema = schema;
272
- if (options.initialized) {
273
- EntityHelper.ensurePropagation(entity);
274
- }
275
- return entity;
276
113
  }
277
- // creates new entity instance, bypassing constructor call as its already persisted entity
278
- const entity = Object.create(meta.class.prototype);
279
- helper(entity).__managed = true;
280
- helper(entity).__processing = !meta.embeddable && !meta.virtual;
281
- helper(entity).__schema = schema;
282
- if (options.merge && !options.newEntity) {
283
- this.#hydrator.hydrateReference(
284
- entity,
285
- meta,
286
- data,
287
- this,
288
- options.convertCustomTypes,
289
- options.schema,
290
- options.parentSchema,
291
- );
292
- this.unitOfWork.register(entity);
293
- }
294
- if (options.initialized) {
295
- EntityHelper.ensurePropagation(entity);
296
- }
297
- return entity;
298
- }
299
- assignDefaultValues(entity, meta) {
300
- for (const prop of meta.props) {
301
- if (prop.embedded || [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
302
- continue;
303
- }
304
- if (prop.onCreate) {
305
- entity[prop.name] ??= prop.onCreate(entity, this.#em);
306
- } else if (prop.default != null && !isRaw(prop.default) && entity[prop.name] === undefined) {
307
- entity[prop.name] = prop.default;
308
- }
309
- if (prop.kind === ReferenceKind.EMBEDDED && entity[prop.name]) {
310
- const items = prop.array ? entity[prop.name] : [entity[prop.name]];
311
- for (const item of items) {
312
- this.assignDefaultValues(item, prop.targetMeta);
114
+ /** Merges new data into an existing entity, preserving user-modified properties. */
115
+ mergeData(meta, entity, data, options = {}) {
116
+ // merge unchanged properties automatically
117
+ data = QueryHelper.processParams(data);
118
+ const existsData = this.#comparator.prepareEntity(entity);
119
+ const originalEntityData = helper(entity).__originalEntityData ?? {};
120
+ const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData);
121
+ // version properties are not part of entity snapshots
122
+ if (meta.versionProperty &&
123
+ data[meta.versionProperty] &&
124
+ data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
125
+ diff[meta.versionProperty] = data[meta.versionProperty];
313
126
  }
314
- }
315
- }
316
- }
317
- hydrate(entity, meta, data, options) {
318
- if (options.initialized) {
319
- this.#hydrator.hydrate(
320
- entity,
321
- meta,
322
- data,
323
- this,
324
- 'full',
325
- options.newEntity,
326
- options.convertCustomTypes,
327
- options.schema,
328
- this.#driver.getSchemaName(meta, options),
329
- options.normalizeAccessors,
330
- );
331
- } else {
332
- this.#hydrator.hydrateReference(
333
- entity,
334
- meta,
335
- data,
336
- this,
337
- options.convertCustomTypes,
338
- options.schema,
339
- this.#driver.getSchemaName(meta, options),
340
- options.normalizeAccessors,
341
- );
342
- }
343
- Utils.keys(data).forEach(key => {
344
- helper(entity)?.__loadedProperties.add(key);
345
- helper(entity)?.__serializationContext.fields?.add(key);
346
- });
347
- const processOnCreateHooksEarly =
348
- options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
349
- if (options.newEntity && processOnCreateHooksEarly) {
350
- this.assignDefaultValues(entity, meta);
127
+ const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
128
+ // do not override values changed by user
129
+ Utils.keys(diff).forEach(key => delete diff2[key]);
130
+ Utils.keys(diff2)
131
+ .filter(key => {
132
+ // ignore null values if there is already present non-null value
133
+ if (existsData[key] != null) {
134
+ return diff2[key] == null;
135
+ }
136
+ return diff2[key] === undefined;
137
+ })
138
+ .forEach(key => delete diff2[key]);
139
+ // but always add collection properties and formulas if they are part of the `data`
140
+ Utils.keys(data)
141
+ .filter(key => meta.properties[key]?.formula ||
142
+ [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
143
+ .forEach(key => (diff2[key] = data[key]));
144
+ // rehydrated with the new values, skip those changed by user
145
+ // use full hydration if the entity is already initialized, even if the caller used `initialized: false`
146
+ // (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
147
+ const initialized = options.initialized || helper(entity).__initialized;
148
+ this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
149
+ // we need to update the entity data only with keys that were not present before
150
+ const nullVal = this.#config.get('forceUndefined') ? undefined : null;
151
+ Utils.keys(diff2).forEach(key => {
152
+ const prop = meta.properties[key];
153
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
154
+ Utils.isPlainObject(data[prop.name])) {
155
+ // oxfmt-ignore
156
+ diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
157
+ }
158
+ if (!options.convertCustomTypes &&
159
+ [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
160
+ prop.customType?.ensureComparable(meta, prop) &&
161
+ diff2[key] != null) {
162
+ const converted = prop.customType.convertToJSValue(diff2[key], this.#platform, { force: true });
163
+ diff2[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { fromQuery: true });
164
+ }
165
+ originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
166
+ helper(entity).__loadedProperties.add(key);
167
+ });
168
+ // in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
169
+ meta.relations.forEach(prop => {
170
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
171
+ Array.isArray(data[prop.name])) {
172
+ // instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
173
+ // we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
174
+ data[prop.name]
175
+ .filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
176
+ .forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
177
+ return;
178
+ }
179
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
180
+ Utils.isPlainObject(data[prop.name]) &&
181
+ entity[prop.name] &&
182
+ helper(entity[prop.name]).__initialized) {
183
+ this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
184
+ }
185
+ });
186
+ this.unitOfWork.normalizeEntityData(meta, originalEntityData);
351
187
  }
352
- }
353
- findEntity(data, meta, options) {
354
- const schema = this.#driver.getSchemaName(meta, options);
355
- if (meta.simplePK) {
356
- return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
188
+ /** Creates or retrieves an uninitialized entity reference by its primary key or alternate key. */
189
+ createReference(entityName, id, options = {}) {
190
+ options.convertCustomTypes ??= true;
191
+ const meta = this.#metadata.get(entityName);
192
+ const schema = this.#driver.getSchemaName(meta, options);
193
+ // Handle alternate key lookup
194
+ if (options.key) {
195
+ const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
196
+ const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
197
+ if (exists) {
198
+ return exists;
199
+ }
200
+ // Create entity stub - storeByKey will set the alternate key property and store in identity map
201
+ const entity = this.create(entityName, {}, { ...options, initialized: false });
202
+ this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
203
+ return entity;
204
+ }
205
+ if (meta.simplePK) {
206
+ const exists = this.unitOfWork.getById(entityName, id, schema);
207
+ if (exists) {
208
+ return exists;
209
+ }
210
+ const data = Utils.isPlainObject(id) ? id : { [meta.primaryKeys[0]]: Array.isArray(id) ? id[0] : id };
211
+ return this.create(entityName, data, { ...options, initialized: false });
212
+ }
213
+ if (Array.isArray(id)) {
214
+ id = Utils.getPrimaryKeyCondFromArray(id, meta);
215
+ }
216
+ const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
217
+ const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
218
+ if (exists) {
219
+ return exists;
220
+ }
221
+ if (Utils.isPrimaryKey(id)) {
222
+ id = { [meta.primaryKeys[0]]: id };
223
+ }
224
+ return this.create(entityName, id, { ...options, initialized: false });
357
225
  }
358
- if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
359
- return undefined;
226
+ /** Creates an embeddable entity instance from the provided data. */
227
+ createEmbeddable(entityName, data, options = {}) {
228
+ data = { ...data };
229
+ const meta = this.#metadata.get(entityName);
230
+ const meta2 = this.processDiscriminatorColumn(meta, data);
231
+ return this.createEntity(data, meta2, options);
360
232
  }
361
- const pks = Utils.getOrderedPrimaryKeys(data, meta, this.#platform, options.convertCustomTypes);
362
- return this.unitOfWork.getById(meta.class, pks, schema);
363
- }
364
- processDiscriminatorColumn(meta, data) {
365
- // Handle STI discriminator (persisted column)
366
- if (meta.root.inheritanceType === 'sti') {
367
- const prop = meta.properties[meta.root.discriminatorColumn];
368
- const value = data[prop.name];
369
- const type = meta.root.discriminatorMap[value];
370
- meta = type ? this.#metadata.get(type) : meta;
371
- return meta;
233
+ /** Returns the EntityComparator instance used for diffing entities. */
234
+ getComparator() {
235
+ return this.#comparator;
372
236
  }
373
- // Handle TPT discriminator (computed at query time)
374
- if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
375
- const value = data[meta.root.tptDiscriminatorColumn];
376
- if (value) {
377
- const type = meta.root.discriminatorMap[value];
378
- meta = type ? this.#metadata.get(type) : meta;
379
- }
237
+ createEntity(data, meta, options) {
238
+ const schema = this.#driver.getSchemaName(meta, options);
239
+ if (options.newEntity || meta.forceConstructor || meta.virtual) {
240
+ if (meta.polymorphs) {
241
+ throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
242
+ }
243
+ const params = this.extractConstructorParams(meta, data, options);
244
+ const Entity = meta.class;
245
+ // creates new instance via constructor as this is the new entity
246
+ const entity = new Entity(...params);
247
+ // creating managed entity instance when `forceEntityConstructor` is enabled,
248
+ // we need to wipe all the values as they would cause update queries on next flush
249
+ if (!options.newEntity && (meta.forceConstructor || this.#config.get('forceEntityConstructor'))) {
250
+ meta.props
251
+ .filter(prop => prop.persist !== false && !prop.primary && data[prop.name] === undefined)
252
+ .forEach(prop => delete entity[prop.name]);
253
+ }
254
+ if (meta.virtual) {
255
+ return entity;
256
+ }
257
+ helper(entity).__schema = schema;
258
+ if (options.initialized) {
259
+ EntityHelper.ensurePropagation(entity);
260
+ }
261
+ return entity;
262
+ }
263
+ // creates new entity instance, bypassing constructor call as its already persisted entity
264
+ const entity = Object.create(meta.class.prototype);
265
+ helper(entity).__managed = true;
266
+ helper(entity).__processing = !meta.embeddable && !meta.virtual;
267
+ helper(entity).__schema = schema;
268
+ if (options.merge && !options.newEntity) {
269
+ this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, options.parentSchema);
270
+ this.unitOfWork.register(entity);
271
+ }
272
+ if (options.initialized) {
273
+ EntityHelper.ensurePropagation(entity);
274
+ }
275
+ return entity;
380
276
  }
381
- return meta;
382
- }
383
- /**
384
- * denormalize PK to value required by driver (e.g. ObjectId)
385
- */
386
- denormalizePrimaryKey(meta, data) {
387
- const pk = meta.getPrimaryProp();
388
- const spk = meta.properties[meta.serializedPrimaryKey];
389
- if (!spk?.serializedPrimaryKey) {
390
- return;
277
+ assignDefaultValues(entity, meta) {
278
+ for (const prop of meta.props) {
279
+ if (prop.embedded || [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
280
+ continue;
281
+ }
282
+ if (prop.onCreate) {
283
+ entity[prop.name] ??= prop.onCreate(entity, this.#em);
284
+ }
285
+ else if (prop.default != null && !isRaw(prop.default) && entity[prop.name] === undefined) {
286
+ entity[prop.name] = prop.default;
287
+ }
288
+ if (prop.kind === ReferenceKind.EMBEDDED && entity[prop.name]) {
289
+ const items = prop.array ? entity[prop.name] : [entity[prop.name]];
290
+ for (const item of items) {
291
+ this.assignDefaultValues(item, prop.targetMeta);
292
+ }
293
+ }
294
+ }
391
295
  }
392
- if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
393
- data[pk.name] = this.#platform.denormalizePrimaryKey(data[spk.name] || data[pk.name]);
394
- delete data[spk.name];
296
+ hydrate(entity, meta, data, options) {
297
+ if (options.initialized) {
298
+ this.#hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
299
+ }
300
+ else {
301
+ this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
302
+ }
303
+ Utils.keys(data).forEach(key => {
304
+ helper(entity)?.__loadedProperties.add(key);
305
+ helper(entity)?.__serializationContext.fields?.add(key);
306
+ });
307
+ const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
308
+ if (options.newEntity && processOnCreateHooksEarly) {
309
+ this.assignDefaultValues(entity, meta);
310
+ }
395
311
  }
396
- }
397
- /**
398
- * returns parameters for entity constructor, creating references from plain ids
399
- */
400
- extractConstructorParams(meta, data, options) {
401
- if (!meta.constructorParams) {
402
- return [data];
312
+ findEntity(data, meta, options) {
313
+ const schema = this.#driver.getSchemaName(meta, options);
314
+ if (meta.simplePK) {
315
+ return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
316
+ }
317
+ if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
318
+ return undefined;
319
+ }
320
+ const pks = Utils.getOrderedPrimaryKeys(data, meta, this.#platform, options.convertCustomTypes);
321
+ return this.unitOfWork.getById(meta.class, pks, schema);
403
322
  }
404
- return meta.constructorParams.map(k => {
405
- const prop = meta.properties[k];
406
- const value = data[k];
407
- if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
408
- const pk = Reference.unwrapReference(value);
409
- const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
410
- if (entity) {
411
- return entity;
323
+ processDiscriminatorColumn(meta, data) {
324
+ // Handle STI discriminator (persisted column)
325
+ if (meta.root.inheritanceType === 'sti') {
326
+ const prop = meta.properties[meta.root.discriminatorColumn];
327
+ const value = data[prop.name];
328
+ const type = meta.root.discriminatorMap[value];
329
+ meta = type ? this.#metadata.get(type) : meta;
330
+ return meta;
412
331
  }
413
- if (Utils.isEntity(value)) {
414
- return value;
332
+ // Handle TPT discriminator (computed at query time)
333
+ if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
334
+ const value = data[meta.root.tptDiscriminatorColumn];
335
+ if (value) {
336
+ const type = meta.root.discriminatorMap[value];
337
+ meta = type ? this.#metadata.get(type) : meta;
338
+ }
415
339
  }
416
- const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
417
- if (Utils.isObject(value) && !nakedPk) {
418
- return this.create(prop.targetMeta.class, value, options);
340
+ return meta;
341
+ }
342
+ /**
343
+ * denormalize PK to value required by driver (e.g. ObjectId)
344
+ */
345
+ denormalizePrimaryKey(meta, data) {
346
+ const pk = meta.getPrimaryProp();
347
+ const spk = meta.properties[meta.serializedPrimaryKey];
348
+ if (!spk?.serializedPrimaryKey) {
349
+ return;
419
350
  }
420
- const { newEntity, initialized, ...rest } = options;
421
- const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
422
- return Reference.wrapReference(target, prop);
423
- }
424
- if (prop?.kind === ReferenceKind.EMBEDDED && value) {
425
- /* v8 ignore next */
426
- if (Utils.isEntity(value)) {
427
- return value;
351
+ if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
352
+ data[pk.name] = this.#platform.denormalizePrimaryKey((data[spk.name] || data[pk.name]));
353
+ delete data[spk.name];
428
354
  }
429
- return this.createEmbeddable(prop.targetMeta.class, value, options);
430
- }
431
- if (!prop) {
432
- const tmp = { ...data };
433
- for (const prop of meta.props) {
434
- if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
435
- continue;
436
- }
437
- if (
438
- [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
439
- Utils.isPlainObject(tmp[prop.name]) &&
440
- !Utils.extractPK(tmp[prop.name], prop.targetMeta, true)
441
- ) {
442
- tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
443
- } else if (prop.kind === ReferenceKind.SCALAR) {
444
- tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
445
- }
355
+ }
356
+ /**
357
+ * returns parameters for entity constructor, creating references from plain ids
358
+ */
359
+ extractConstructorParams(meta, data, options) {
360
+ if (!meta.constructorParams) {
361
+ return [data];
446
362
  }
447
- return tmp;
448
- }
449
- if (options.convertCustomTypes && prop.customType && value != null) {
450
- return prop.customType.convertToJSValue(value, this.#platform);
451
- }
452
- return value;
453
- });
454
- }
455
- get unitOfWork() {
456
- return this.#em.getUnitOfWork(false);
457
- }
363
+ return meta.constructorParams.map(k => {
364
+ const prop = meta.properties[k];
365
+ const value = data[k];
366
+ if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
367
+ const pk = Reference.unwrapReference(value);
368
+ const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
369
+ if (entity) {
370
+ return entity;
371
+ }
372
+ if (Utils.isEntity(value)) {
373
+ return value;
374
+ }
375
+ const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
376
+ if (Utils.isObject(value) && !nakedPk) {
377
+ return this.create(prop.targetMeta.class, value, options);
378
+ }
379
+ const { newEntity, initialized, ...rest } = options;
380
+ const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
381
+ return Reference.wrapReference(target, prop);
382
+ }
383
+ if (prop?.kind === ReferenceKind.EMBEDDED && value) {
384
+ /* v8 ignore next */
385
+ if (Utils.isEntity(value)) {
386
+ return value;
387
+ }
388
+ return this.createEmbeddable(prop.targetMeta.class, value, options);
389
+ }
390
+ if (!prop) {
391
+ const tmp = { ...data };
392
+ for (const prop of meta.props) {
393
+ if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
394
+ continue;
395
+ }
396
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
397
+ Utils.isPlainObject(tmp[prop.name]) &&
398
+ !Utils.extractPK(tmp[prop.name], prop.targetMeta, true)) {
399
+ tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
400
+ }
401
+ else if (prop.kind === ReferenceKind.SCALAR) {
402
+ tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
403
+ }
404
+ }
405
+ return tmp;
406
+ }
407
+ if (options.convertCustomTypes && prop.customType && value != null) {
408
+ return prop.customType.convertToJSValue(value, this.#platform);
409
+ }
410
+ return value;
411
+ });
412
+ }
413
+ get unitOfWork() {
414
+ return this.#em.getUnitOfWork(false);
415
+ }
458
416
  }