@mikro-orm/core 7.0.7-dev.9 → 7.0.7

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