Merges new data into an existing entity, preserving user-modified properties.
(
meta: EntityMetadata<T>,
entity: T,
data: EntityData<T>,
options: FactoryOptions = {},
)
| 218 | |
| 219 | /** Merges new data into an existing entity, preserving user-modified properties. */ |
| 220 | mergeData<T extends object>( |
| 221 | meta: EntityMetadata<T>, |
| 222 | entity: T, |
| 223 | data: EntityData<T>, |
| 224 | options: FactoryOptions = {}, |
| 225 | ): void { |
| 226 | // merge unchanged properties automatically |
| 227 | data = QueryHelper.processParams(data); |
| 228 | const existsData = this.#comparator.prepareEntity(entity); |
| 229 | const originalEntityData = helper(entity).__originalEntityData ?? ({} as EntityData<T>); |
| 230 | const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData); |
| 231 | |
| 232 | const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true }); |
| 233 | |
| 234 | // do not override values changed by user; for uninitialized entities, |
| 235 | // the diff may include stale snapshot entries (value is undefined) — skip those |
| 236 | if (helper(entity).__initialized) { |
| 237 | Utils.keys(diff).forEach(key => delete diff2[key]); |
| 238 | } else { |
| 239 | Utils.keys(diff).forEach(key => { |
| 240 | if (diff[key] !== undefined) { |
| 241 | delete diff2[key]; |
| 242 | } |
| 243 | }); |
| 244 | } |
| 245 | |
| 246 | Utils.keys(diff2) |
| 247 | .filter(key => { |
| 248 | // ignore null values if there is already present non-null value |
| 249 | if (existsData[key] != null) { |
| 250 | return diff2[key] == null; |
| 251 | } |
| 252 | |
| 253 | return diff2[key] === undefined; |
| 254 | }) |
| 255 | .forEach(key => delete diff2[key]); |
| 256 | |
| 257 | // but always add collection properties, formulas, generated columns, version properties, and the |
| 258 | // user-declared STI discriminator if they are part of the `data`, as these are excluded from |
| 259 | // `comparableProps` and won't appear in the diff |
| 260 | Utils.keys(data) |
| 261 | .filter( |
| 262 | key => |
| 263 | meta.properties[key]?.formula || |
| 264 | meta.properties[key]?.version || |
| 265 | (meta.properties[key]?.generated && !meta.properties[key]?.primary) || |
| 266 | [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind) || |
| 267 | (key === meta.root.discriminatorColumn && meta.properties[key]?.userDefined !== false), |
| 268 | ) |
| 269 | .forEach(key => (diff2[key] = data[key])); |
| 270 | |
| 271 | // rehydrated with the new values, skip those changed by user |
| 272 | // use full hydration if the entity is already initialized, even if the caller used `initialized: false` |
| 273 | // (e.g. from createReference), otherwise scalar properties in diff2 won't be applied |
| 274 | const initialized = options.initialized || helper(entity).__initialized; |
| 275 | this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options); |
| 276 | |
| 277 | // we need to update the entity data only with keys that were not present before |
no test coverage detected