@mikro-orm/core 7.0.9-dev.1 → 7.0.9-dev.11

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.
@@ -310,7 +310,7 @@ export class DatabaseDriver {
310
310
  if (prop.joinColumns && Array.isArray(data[k])) {
311
311
  const copy = Utils.flatten(data[k]);
312
312
  delete data[k];
313
- prop.joinColumns.forEach((joinColumn, idx) => (data[joinColumn] = copy[idx]));
313
+ (prop.ownColumns ?? prop.joinColumns).forEach(col => (data[col] = copy[prop.joinColumns.indexOf(col)]));
314
314
  return;
315
315
  }
316
316
  if (prop.joinColumns?.length > 1 && data[k] == null) {
@@ -167,9 +167,11 @@ export class EntityFactory {
167
167
  return diff2[key] === undefined;
168
168
  })
169
169
  .forEach(key => delete diff2[key]);
170
- // but always add collection properties and formulas if they are part of the `data`
170
+ // but always add collection properties, formulas, and generated columns if they are part of the `data`,
171
+ // as these are excluded from `comparableProps` and won't appear in the diff
171
172
  Utils.keys(data)
172
173
  .filter(key => meta.properties[key]?.formula ||
174
+ (meta.properties[key]?.generated && !meta.properties[key]?.primary) ||
173
175
  [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
174
176
  .forEach(key => (diff2[key] = data[key]));
175
177
  // rehydrated with the new values, skip those changed by user
@@ -1267,6 +1267,18 @@ export class MetadataDiscovery {
1267
1267
  if (prop.enum && prop.items && rootProp?.items) {
1268
1268
  newProp.items = Utils.unique([...rootProp.items, ...prop.items]);
1269
1269
  }
1270
+ // When multiple STI children share the same unique relation (OneToOne/ManyToOne),
1271
+ // replace the simple unique with a composite one including the discriminator column
1272
+ // so different subtypes can independently reference the same target row.
1273
+ if (rootProp?.unique &&
1274
+ newProp.unique &&
1275
+ [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(newProp.kind)) {
1276
+ newProp.unique = false;
1277
+ rootProp.unique = false;
1278
+ meta.root.uniques.push({
1279
+ properties: [prop.name, meta.root.discriminatorColumn],
1280
+ });
1281
+ }
1270
1282
  newProp.nullable = true;
1271
1283
  newProp.inherited = !rootProp;
1272
1284
  meta.root.addProperty(newProp);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.0.9-dev.1",
3
+ "version": "7.0.9-dev.11",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -323,14 +323,47 @@ export class Platform {
323
323
  }
324
324
  /** Serializes a string array into its database storage format. */
325
325
  marshallArray(values) {
326
- return values.join(',');
326
+ return values.map(v => (/[,",\\]/.test(v) ? JSON.stringify(v) : v)).join(',');
327
327
  }
328
328
  /** Deserializes a database-stored array string back into a string array. */
329
329
  unmarshallArray(value) {
330
330
  if (value === '') {
331
331
  return [];
332
332
  }
333
- return value.split(',');
333
+ if (!value.includes('"')) {
334
+ return value.split(',');
335
+ }
336
+ const result = [];
337
+ let i = 0;
338
+ while (i < value.length) {
339
+ if (value[i] === '"') {
340
+ let j = i + 1;
341
+ while (j < value.length) {
342
+ if (value[j] === '\\') {
343
+ j += 2;
344
+ }
345
+ else if (value[j] === '"') {
346
+ j++;
347
+ break;
348
+ }
349
+ else {
350
+ j++;
351
+ }
352
+ }
353
+ result.push(JSON.parse(value.substring(i, j)));
354
+ i = j + 1;
355
+ }
356
+ else {
357
+ const comma = value.indexOf(',', i);
358
+ if (comma === -1) {
359
+ result.push(value.substring(i));
360
+ break;
361
+ }
362
+ result.push(value.substring(i, comma));
363
+ i = comma + 1;
364
+ }
365
+ }
366
+ return result;
334
367
  }
335
368
  getBlobDeclarationSQL() {
336
369
  return 'blob';
@@ -24,6 +24,12 @@ export declare class ChangeSetPersister {
24
24
  private checkConcurrencyKeys;
25
25
  private persistManagedEntitiesBatch;
26
26
  private mapPrimaryKey;
27
+ /**
28
+ * After INSERT + hydration, sync all EntityIdentifier placeholders in composite PK arrays
29
+ * with the real values now present on the entity. This is needed because `mapPrimaryKey`
30
+ * only handles the first PK column, but any scalar PK in a composite key may be auto-generated.
31
+ */
32
+ private syncCompositeIdentifiers;
27
33
  /**
28
34
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
29
35
  */
@@ -132,6 +132,7 @@ export class ChangeSetPersister {
132
132
  this.mapPrimaryKey(meta, res.insertId ?? res.row?.[meta.primaryKeys[0]], changeSet);
133
133
  }
134
134
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
135
+ this.syncCompositeIdentifiers(changeSet);
135
136
  this.markAsPopulated(changeSet, meta);
136
137
  wrapped.__initialized = true;
137
138
  wrapped.__managed = true;
@@ -178,6 +179,7 @@ export class ChangeSetPersister {
178
179
  if (res.rows) {
179
180
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.rows[i], meta);
180
181
  }
182
+ this.syncCompositeIdentifiers(changeSet);
181
183
  this.markAsPopulated(changeSet, meta);
182
184
  wrapped.__initialized = true;
183
185
  wrapped.__managed = true;
@@ -255,6 +257,24 @@ export class ChangeSetPersister {
255
257
  wrapped.__identifier.setValue(value);
256
258
  }
257
259
  }
260
+ /**
261
+ * After INSERT + hydration, sync all EntityIdentifier placeholders in composite PK arrays
262
+ * with the real values now present on the entity. This is needed because `mapPrimaryKey`
263
+ * only handles the first PK column, but any scalar PK in a composite key may be auto-generated.
264
+ */
265
+ syncCompositeIdentifiers(changeSet) {
266
+ const wrapped = helper(changeSet.entity);
267
+ if (!Array.isArray(wrapped.__identifier)) {
268
+ return;
269
+ }
270
+ const pks = changeSet.meta.getPrimaryProps();
271
+ for (let i = 0; i < pks.length; i++) {
272
+ const ident = wrapped.__identifier[i];
273
+ if (ident instanceof EntityIdentifier && pks[i].kind === ReferenceKind.SCALAR) {
274
+ ident.setValue(changeSet.entity[pks[i].name]);
275
+ }
276
+ }
277
+ }
258
278
  /**
259
279
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
260
280
  */
@@ -413,8 +433,8 @@ export class ChangeSetPersister {
413
433
  }
414
434
  return;
415
435
  }
416
- if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
417
- changeSet.payload[prop.name] = value.map(item => item.getValue());
436
+ if (Array.isArray(value) && value.some(item => item instanceof EntityIdentifier)) {
437
+ changeSet.payload[prop.name] = value.map(item => (item instanceof EntityIdentifier ? item.getValue() : item));
418
438
  return;
419
439
  }
420
440
  if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
package/utils/Utils.js CHANGED
@@ -132,7 +132,7 @@ export function parseJsonSafe(value) {
132
132
  /** Collection of general-purpose utility methods used throughout the ORM. */
133
133
  export class Utils {
134
134
  static PK_SEPARATOR = '~~~';
135
- static #ORM_VERSION = '7.0.9-dev.1';
135
+ static #ORM_VERSION = '7.0.9-dev.11';
136
136
  /**
137
137
  * Checks if the argument is instance of `Object`. Returns false for arrays.
138
138
  */