@mikro-orm/core 7.0.0-dev.4 → 7.0.0-dev.40
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.
- package/EntityManager.d.ts +84 -18
- package/EntityManager.js +265 -172
- package/MikroORM.d.ts +7 -5
- package/MikroORM.js +0 -1
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +2 -1
- package/cache/FileCacheAdapter.js +6 -4
- package/connections/Connection.d.ts +4 -2
- package/connections/Connection.js +2 -2
- package/decorators/Check.d.ts +2 -2
- package/decorators/Embeddable.d.ts +5 -5
- package/decorators/Embeddable.js +1 -1
- package/decorators/Embedded.d.ts +6 -12
- package/decorators/Entity.d.ts +18 -3
- package/decorators/Enum.d.ts +1 -1
- package/decorators/Formula.d.ts +1 -2
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +4 -2
- package/decorators/ManyToOne.d.ts +6 -2
- package/decorators/OneToMany.d.ts +4 -4
- package/decorators/OneToOne.d.ts +5 -1
- package/decorators/PrimaryKey.d.ts +2 -3
- package/decorators/Property.d.ts +54 -4
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/decorators/index.d.ts +1 -1
- package/drivers/DatabaseDriver.d.ts +4 -3
- package/drivers/IDatabaseDriver.d.ts +22 -2
- package/entity/ArrayCollection.d.ts +4 -2
- package/entity/ArrayCollection.js +18 -6
- package/entity/Collection.d.ts +1 -2
- package/entity/Collection.js +19 -10
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +9 -1
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +29 -9
- package/entity/EntityHelper.js +25 -3
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +74 -37
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +1 -1
- package/entity/Reference.d.ts +9 -7
- package/entity/Reference.js +30 -3
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +561 -0
- package/entity/defineEntity.js +537 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +16 -3
- package/enums.js +13 -0
- package/errors.d.ts +6 -0
- package/errors.js +14 -0
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +35 -24
- package/index.d.ts +2 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/SimpleLogger.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +8 -4
- package/metadata/EntitySchema.js +39 -19
- package/metadata/MetadataDiscovery.d.ts +1 -1
- package/metadata/MetadataDiscovery.js +88 -32
- package/metadata/MetadataStorage.js +1 -1
- package/metadata/MetadataValidator.js +4 -3
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +7 -1
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/package.json +5 -5
- package/platforms/Platform.d.ts +5 -3
- package/platforms/Platform.js +4 -8
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +2 -2
- package/serialization/EntityTransformer.js +1 -1
- package/serialization/SerializationContext.js +14 -11
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +3 -0
- package/types/BooleanType.d.ts +1 -1
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/Type.d.ts +2 -1
- package/types/Type.js +1 -1
- package/types/index.d.ts +1 -1
- package/typings.d.ts +88 -39
- package/typings.js +24 -4
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +37 -16
- package/unit-of-work/UnitOfWork.d.ts +8 -1
- package/unit-of-work/UnitOfWork.js +109 -41
- package/utils/Configuration.d.ts +23 -5
- package/utils/Configuration.js +17 -3
- package/utils/ConfigurationLoader.d.ts +0 -2
- package/utils/ConfigurationLoader.js +2 -24
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +7 -2
- package/utils/DataloaderUtils.js +38 -7
- package/utils/EntityComparator.d.ts +6 -2
- package/utils/EntityComparator.js +104 -58
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +66 -5
- package/utils/RawQueryFragment.d.ts +36 -2
- package/utils/RawQueryFragment.js +35 -1
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +218 -0
- package/utils/Utils.d.ts +11 -5
- package/utils/Utils.js +76 -33
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.d.ts +7 -2
- package/utils/upsert-utils.js +52 -1
package/entity/EntityLoader.js
CHANGED
|
@@ -32,13 +32,12 @@ export class EntityLoader {
|
|
|
32
32
|
const visited = options.visited ??= new Set();
|
|
33
33
|
options.where ??= {};
|
|
34
34
|
options.orderBy ??= {};
|
|
35
|
-
options.filters ??= {};
|
|
36
35
|
options.lookup ??= true;
|
|
37
36
|
options.validate ??= true;
|
|
38
37
|
options.refresh ??= false;
|
|
39
38
|
options.convertCustomTypes ??= true;
|
|
40
39
|
if (references.length > 0) {
|
|
41
|
-
await this.populateScalar(meta, references, options);
|
|
40
|
+
await this.populateScalar(meta, references, { ...options, populateWhere: undefined });
|
|
42
41
|
}
|
|
43
42
|
populate = this.normalizePopulate(entityName, populate, options.strategy, options.lookup);
|
|
44
43
|
const invalid = populate.find(({ field }) => !this.em.canPopulate(entityName, field));
|
|
@@ -140,17 +139,22 @@ export class EntityLoader {
|
|
|
140
139
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
141
140
|
.filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) || Utils.isObject(orderBy[prop.name]))
|
|
142
141
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
142
|
+
const where = await this.extractChildCondition(options, prop);
|
|
143
143
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
|
|
144
|
-
|
|
144
|
+
const res = await this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
|
|
145
|
+
return Utils.flatten(res);
|
|
145
146
|
}
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
148
|
+
...options,
|
|
149
|
+
where,
|
|
150
|
+
orderBy: innerOrderBy,
|
|
151
|
+
}, !!(ref || prop.mapToPk));
|
|
152
|
+
this.initializeCollections(filtered, prop, field, items, innerOrderBy.length > 0, partial);
|
|
153
|
+
return items;
|
|
150
154
|
}
|
|
151
155
|
async populateScalar(meta, filtered, options) {
|
|
152
156
|
const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
153
|
-
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta
|
|
157
|
+
const ids = Utils.unique(filtered.map(e => Utils.getPrimaryKeyValues(e, meta, true)));
|
|
154
158
|
const where = this.mergePrimaryCondition(ids, pk, options, meta, this.metadata, this.driver.getPlatform());
|
|
155
159
|
const { filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, fields } = options;
|
|
156
160
|
await this.em.find(meta.className, where, {
|
|
@@ -159,15 +163,15 @@ export class EntityLoader {
|
|
|
159
163
|
populate: [],
|
|
160
164
|
});
|
|
161
165
|
}
|
|
162
|
-
initializeCollections(filtered, prop, field, children, customOrder) {
|
|
166
|
+
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
163
167
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
164
|
-
this.initializeOneToMany(filtered, children, prop, field);
|
|
168
|
+
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
165
169
|
}
|
|
166
170
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && !this.driver.getPlatform().usesPivotTable()) {
|
|
167
|
-
this.initializeManyToMany(filtered, children, prop, field, customOrder);
|
|
171
|
+
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
|
-
initializeOneToMany(filtered, children, prop, field) {
|
|
174
|
+
initializeOneToMany(filtered, children, prop, field, partial) {
|
|
171
175
|
const mapToPk = prop.targetMeta.properties[prop.mappedBy].mapToPk;
|
|
172
176
|
const map = {};
|
|
173
177
|
for (const entity of filtered) {
|
|
@@ -183,14 +187,14 @@ export class EntityLoader {
|
|
|
183
187
|
}
|
|
184
188
|
for (const entity of filtered) {
|
|
185
189
|
const key = helper(entity).getSerializedPrimaryKey();
|
|
186
|
-
entity[field].hydrate(map[key]);
|
|
190
|
+
entity[field].hydrate(map[key], undefined, partial);
|
|
187
191
|
}
|
|
188
192
|
}
|
|
189
|
-
initializeManyToMany(filtered, children, prop, field, customOrder) {
|
|
193
|
+
initializeManyToMany(filtered, children, prop, field, customOrder, partial) {
|
|
190
194
|
if (prop.mappedBy) {
|
|
191
195
|
for (const entity of filtered) {
|
|
192
196
|
const items = children.filter(child => child[prop.mappedBy].contains(entity, false));
|
|
193
|
-
entity[field].hydrate(items, true);
|
|
197
|
+
entity[field].hydrate(items, true, partial);
|
|
194
198
|
}
|
|
195
199
|
}
|
|
196
200
|
else { // owning side of M:N without pivot table needs to be reordered
|
|
@@ -200,15 +204,16 @@ export class EntityLoader {
|
|
|
200
204
|
if (!customOrder) {
|
|
201
205
|
items.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
202
206
|
}
|
|
203
|
-
entity[field].hydrate(items, true);
|
|
207
|
+
entity[field].hydrate(items, true, partial);
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
async findChildren(entities, prop, populate, options, ref) {
|
|
208
|
-
const children = this.getChildReferences(entities, prop, options, ref);
|
|
212
|
+
const children = Utils.unique(this.getChildReferences(entities, prop, options, ref));
|
|
209
213
|
const meta = prop.targetMeta;
|
|
210
214
|
let fk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
211
215
|
let schema = options.schema;
|
|
216
|
+
const partial = !Utils.isEmpty(prop.where) || !Utils.isEmpty(options.where);
|
|
212
217
|
if (prop.kind === ReferenceKind.ONE_TO_MANY || (prop.kind === ReferenceKind.MANY_TO_MANY && !prop.owner)) {
|
|
213
218
|
fk = meta.properties[prop.mappedBy].name;
|
|
214
219
|
}
|
|
@@ -218,7 +223,7 @@ export class EntityLoader {
|
|
|
218
223
|
children.push(...this.filterByReferences(entities, prop.name, options.refresh));
|
|
219
224
|
}
|
|
220
225
|
if (children.length === 0) {
|
|
221
|
-
return [];
|
|
226
|
+
return { items: [], partial };
|
|
222
227
|
}
|
|
223
228
|
if (!schema && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
224
229
|
schema = children.find(e => e.__helper.__schema)?.__helper.__schema;
|
|
@@ -227,7 +232,7 @@ export class EntityLoader {
|
|
|
227
232
|
let where = this.mergePrimaryCondition(ids, fk, options, meta, this.metadata, this.driver.getPlatform());
|
|
228
233
|
const fields = this.buildFields(options.fields, prop, ref);
|
|
229
234
|
/* eslint-disable prefer-const */
|
|
230
|
-
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, } = options;
|
|
235
|
+
let { refresh, filters, convertCustomTypes, lockMode, strategy, populateWhere = 'infer', connectionType, logging, } = options;
|
|
231
236
|
/* eslint-enable prefer-const */
|
|
232
237
|
if (typeof populateWhere === 'object') {
|
|
233
238
|
populateWhere = await this.extractChildCondition({ where: populateWhere }, prop);
|
|
@@ -249,9 +254,13 @@ export class EntityLoader {
|
|
|
249
254
|
}
|
|
250
255
|
}
|
|
251
256
|
}
|
|
257
|
+
const orderBy = [...Utils.asArray(options.orderBy), ...propOrderBy].filter((order, idx, array) => {
|
|
258
|
+
// skip consecutive ordering with the same key to get around mongo issues
|
|
259
|
+
return idx === 0 || !Utils.equals(Object.keys(array[idx - 1]), Object.keys(order));
|
|
260
|
+
});
|
|
252
261
|
const items = await this.em.find(prop.type, where, {
|
|
253
262
|
filters, convertCustomTypes, lockMode, populateWhere, logging,
|
|
254
|
-
orderBy
|
|
263
|
+
orderBy,
|
|
255
264
|
populate: populate.children ?? populate.all ?? [],
|
|
256
265
|
exclude: Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude,
|
|
257
266
|
strategy, fields, schema, connectionType,
|
|
@@ -260,6 +269,24 @@ export class EntityLoader {
|
|
|
260
269
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
261
270
|
visited: options.visited,
|
|
262
271
|
});
|
|
272
|
+
if ([ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
|
|
273
|
+
const nullVal = this.em.config.get('forceUndefined') ? undefined : null;
|
|
274
|
+
const itemsMap = new Set();
|
|
275
|
+
const childrenMap = new Set();
|
|
276
|
+
for (const item of items) {
|
|
277
|
+
itemsMap.add(helper(item).getSerializedPrimaryKey());
|
|
278
|
+
}
|
|
279
|
+
for (const child of children) {
|
|
280
|
+
childrenMap.add(helper(child).getSerializedPrimaryKey());
|
|
281
|
+
}
|
|
282
|
+
for (const entity of entities) {
|
|
283
|
+
const key = helper(entity[prop.name] ?? {})?.getSerializedPrimaryKey();
|
|
284
|
+
if (childrenMap.has(key) && !itemsMap.has(key)) {
|
|
285
|
+
entity[prop.name] = nullVal;
|
|
286
|
+
helper(entity).__originalEntityData[prop.name] = null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
263
290
|
for (const item of items) {
|
|
264
291
|
if (ref && !helper(item).__onLoadFired) {
|
|
265
292
|
helper(item).__initialized = false;
|
|
@@ -267,7 +294,7 @@ export class EntityLoader {
|
|
|
267
294
|
this.em.getUnitOfWork()['loadedEntities'].delete(item);
|
|
268
295
|
}
|
|
269
296
|
}
|
|
270
|
-
return items;
|
|
297
|
+
return { items, partial };
|
|
271
298
|
}
|
|
272
299
|
mergePrimaryCondition(ids, pk, options, meta, metadata, platform) {
|
|
273
300
|
const cond1 = QueryHelper.processWhere({ where: { [pk]: { $in: ids } }, entityName: meta.className, metadata, platform, convertCustomTypes: !options.convertCustomTypes });
|
|
@@ -283,6 +310,7 @@ export class EntityLoader {
|
|
|
283
310
|
if (prop.kind === ReferenceKind.SCALAR && !prop.lazy) {
|
|
284
311
|
return;
|
|
285
312
|
}
|
|
313
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
286
314
|
const populated = await this.populateMany(entityName, entities, populate, options);
|
|
287
315
|
if (!populate.children && !populate.all) {
|
|
288
316
|
return;
|
|
@@ -310,10 +338,18 @@ export class EntityLoader {
|
|
|
310
338
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
311
339
|
.filter(orderBy => Utils.isObject(orderBy[prop.name]))
|
|
312
340
|
.map(orderBy => orderBy[prop.name]);
|
|
313
|
-
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging } = options;
|
|
341
|
+
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging, schema } = options;
|
|
314
342
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
315
|
-
const
|
|
316
|
-
|
|
343
|
+
const visited = options.visited;
|
|
344
|
+
for (const entity of entities) {
|
|
345
|
+
visited.delete(entity);
|
|
346
|
+
}
|
|
347
|
+
const unique = Utils.unique(children);
|
|
348
|
+
const filtered = unique.filter(e => !visited.has(e));
|
|
349
|
+
for (const entity of entities) {
|
|
350
|
+
visited.add(entity);
|
|
351
|
+
}
|
|
352
|
+
await this.populate(prop.type, unique, populate.children ?? populate.all, {
|
|
317
353
|
where: await this.extractChildCondition(options, prop, false),
|
|
318
354
|
orderBy: innerOrderBy,
|
|
319
355
|
fields,
|
|
@@ -325,12 +361,16 @@ export class EntityLoader {
|
|
|
325
361
|
populateWhere,
|
|
326
362
|
connectionType,
|
|
327
363
|
logging,
|
|
364
|
+
schema,
|
|
328
365
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
329
366
|
refresh: refresh && !filtered.every(item => options.visited.has(item)),
|
|
330
367
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
331
368
|
visited: options.visited,
|
|
369
|
+
// @ts-ignore not a public option
|
|
370
|
+
filtered,
|
|
332
371
|
});
|
|
333
372
|
}
|
|
373
|
+
/** @internal */
|
|
334
374
|
async findChildrenFromPivotTable(filtered, prop, options, orderBy, populate, pivotJoin) {
|
|
335
375
|
const ids = filtered.map(e => e.__helper.__primaryKeys);
|
|
336
376
|
const refresh = options.refresh;
|
|
@@ -368,7 +408,7 @@ export class EntityLoader {
|
|
|
368
408
|
return this.em.getUnitOfWork().register(entity, item, { refresh, loaded: true });
|
|
369
409
|
});
|
|
370
410
|
entity[prop.name].hydrate(items, true);
|
|
371
|
-
children.push(
|
|
411
|
+
children.push(items);
|
|
372
412
|
}
|
|
373
413
|
return children;
|
|
374
414
|
}
|
|
@@ -444,23 +484,20 @@ export class EntityLoader {
|
|
|
444
484
|
}
|
|
445
485
|
getChildReferences(entities, prop, options, ref) {
|
|
446
486
|
const filtered = this.filterCollections(entities, prop.name, options, ref);
|
|
447
|
-
const children = [];
|
|
448
487
|
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
449
|
-
|
|
488
|
+
return filtered.map(e => e[prop.name].owner);
|
|
450
489
|
}
|
|
451
|
-
|
|
452
|
-
|
|
490
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner) {
|
|
491
|
+
return filtered.reduce((a, b) => {
|
|
453
492
|
a.push(...b[prop.name].getItems());
|
|
454
493
|
return a;
|
|
455
|
-
}, [])
|
|
494
|
+
}, []);
|
|
456
495
|
}
|
|
457
|
-
|
|
458
|
-
|
|
496
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) { // inverse side
|
|
497
|
+
return filtered;
|
|
459
498
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
return children;
|
|
499
|
+
// MANY_TO_ONE or ONE_TO_ONE
|
|
500
|
+
return this.filterReferences(entities, prop.name, options, ref);
|
|
464
501
|
}
|
|
465
502
|
filterCollections(entities, field, options, ref) {
|
|
466
503
|
if (options.refresh) {
|
|
@@ -514,7 +551,7 @@ export class EntityLoader {
|
|
|
514
551
|
if (refresh) {
|
|
515
552
|
return entities;
|
|
516
553
|
}
|
|
517
|
-
return entities.filter(e => !e[field]?.__helper?.__initialized);
|
|
554
|
+
return entities.filter(e => e[field] !== null && !e[field]?.__helper?.__initialized);
|
|
518
555
|
}
|
|
519
556
|
lookupAllRelationships(entityName) {
|
|
520
557
|
const ret = [];
|
|
@@ -80,7 +80,7 @@ export declare class EntityRepository<Entity extends object> {
|
|
|
80
80
|
/**
|
|
81
81
|
* @inheritDoc EntityManager.findByCursor
|
|
82
82
|
*/
|
|
83
|
-
findByCursor<Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(where: FilterQuery<Entity>, options: FindByCursorOptions<Entity, Hint, Fields, Excludes>): Promise<Cursor<Entity, Hint, Fields, Excludes>>;
|
|
83
|
+
findByCursor<Hint extends string = never, Fields extends string = '*', Excludes extends string = never, IncludeCount extends boolean = true>(where: FilterQuery<Entity>, options: FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>): Promise<Cursor<Entity, Hint, Fields, Excludes, IncludeCount>>;
|
|
84
84
|
/**
|
|
85
85
|
* Finds all entities of given type. You can pass additional options via the `options` parameter.
|
|
86
86
|
*/
|
|
@@ -11,7 +11,7 @@ export class EntityValidator {
|
|
|
11
11
|
}
|
|
12
12
|
validate(entity, payload, meta) {
|
|
13
13
|
meta.props.forEach(prop => {
|
|
14
|
-
if (prop.inherited) {
|
|
14
|
+
if (prop.inherited || (prop.persist === false && prop.userDefined !== false)) {
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
package/entity/Reference.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { inspect } from 'node:util';
|
|
2
|
-
import type { AddEager, Dictionary, EntityClass, EntityKey, EntityProperty, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
2
|
+
import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
3
3
|
import type { FindOneOptions, FindOneOrFailOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
4
|
export declare class Reference<T extends object> {
|
|
5
5
|
private entity;
|
|
6
|
+
private property?;
|
|
6
7
|
constructor(entity: T);
|
|
7
8
|
static create<T extends object>(entity: T | Ref<T>): Ref<T>;
|
|
8
9
|
static createFromPK<T extends object>(entityType: EntityClass<T>, pk: Primary<T>, options?: {
|
|
@@ -56,6 +57,11 @@ export declare class ScalarReference<Value> {
|
|
|
56
57
|
* Returns either the whole entity, or the requested property.
|
|
57
58
|
*/
|
|
58
59
|
load(options?: Omit<LoadReferenceOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value | undefined>;
|
|
60
|
+
/**
|
|
61
|
+
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
62
|
+
* Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
|
|
63
|
+
*/
|
|
64
|
+
loadOrFail(options?: Omit<LoadReferenceOrFailOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value>;
|
|
59
65
|
set(value: Value): void;
|
|
60
66
|
bind<Entity extends object>(entity: Entity, property: EntityKey<Entity>): void;
|
|
61
67
|
unwrap(): Value | undefined;
|
|
@@ -72,15 +78,11 @@ export interface LoadReferenceOrFailOptions<T extends object, P extends string =
|
|
|
72
78
|
/**
|
|
73
79
|
* shortcut for `wrap(entity).toReference()`
|
|
74
80
|
*/
|
|
75
|
-
export declare function ref<
|
|
81
|
+
export declare function ref<I extends unknown | Ref<unknown> | undefined | null, T extends I & {}>(entity: I): Ref<T> & LoadedReference<Loaded<T, AddEager<T>>> | AddOptional<typeof entity>;
|
|
76
82
|
/**
|
|
77
83
|
* shortcut for `Reference.createFromPK(entityType, pk)`
|
|
78
84
|
*/
|
|
79
|
-
export declare function ref<T, PKV extends Primary<T> = Primary<T>>(entityType: EntityClass<T>, pk
|
|
80
|
-
/**
|
|
81
|
-
* shortcut for `wrap(entity).toReference()`
|
|
82
|
-
*/
|
|
83
|
-
export declare function ref<T>(value: T | Ref<T>): Ref<T> & LoadedReference<Loaded<T, AddEager<T>>>;
|
|
85
|
+
export declare function ref<I extends unknown | undefined | null, T, PKV extends Primary<T> = Primary<T>>(entityType: EntityClass<T>, pk: I): Ref<T> | AddOptional<typeof pk>;
|
|
84
86
|
/**
|
|
85
87
|
* shortcut for `Reference.createNakedFromPK(entityType, pk)`
|
|
86
88
|
*/
|
package/entity/Reference.js
CHANGED
|
@@ -2,8 +2,11 @@ import { inspect } from 'node:util';
|
|
|
2
2
|
import { DataloaderType } from '../enums.js';
|
|
3
3
|
import { helper, wrap } from './wrap.js';
|
|
4
4
|
import { Utils } from '../utils/Utils.js';
|
|
5
|
+
import { QueryHelper } from '../utils/QueryHelper.js';
|
|
6
|
+
import { NotFoundError } from '../errors.js';
|
|
5
7
|
export class Reference {
|
|
6
8
|
entity;
|
|
9
|
+
property;
|
|
7
10
|
constructor(entity) {
|
|
8
11
|
this.entity = entity;
|
|
9
12
|
this.set(entity);
|
|
@@ -33,10 +36,15 @@ export class Reference {
|
|
|
33
36
|
}
|
|
34
37
|
static createFromPK(entityType, pk, options) {
|
|
35
38
|
const ref = this.createNakedFromPK(entityType, pk, options);
|
|
36
|
-
return helper(ref)
|
|
39
|
+
return helper(ref)?.toReference() ?? ref;
|
|
37
40
|
}
|
|
38
41
|
static createNakedFromPK(entityType, pk, options) {
|
|
39
42
|
const factory = entityType.prototype.__factory;
|
|
43
|
+
if (!factory) {
|
|
44
|
+
// this can happen only if `ref()` is used as a property initializer, and the value is important only for the
|
|
45
|
+
// inference of defaults, so it's fine to return it directly without wrapping with `Reference` class
|
|
46
|
+
return pk;
|
|
47
|
+
}
|
|
40
48
|
const entity = factory.createReference(entityType, pk, {
|
|
41
49
|
merge: false,
|
|
42
50
|
convertCustomTypes: false,
|
|
@@ -58,7 +66,9 @@ export class Reference {
|
|
|
58
66
|
*/
|
|
59
67
|
static wrapReference(entity, prop) {
|
|
60
68
|
if (entity && prop.ref && !Reference.isReference(entity)) {
|
|
61
|
-
|
|
69
|
+
const ref = Reference.create(entity);
|
|
70
|
+
ref.property = prop;
|
|
71
|
+
return ref;
|
|
62
72
|
}
|
|
63
73
|
return entity;
|
|
64
74
|
}
|
|
@@ -78,6 +88,7 @@ export class Reference {
|
|
|
78
88
|
if (!wrapped.__em) {
|
|
79
89
|
return this.entity;
|
|
80
90
|
}
|
|
91
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property?.filters, options.filters) };
|
|
81
92
|
if (this.isInitialized() && !options.refresh && options.populate) {
|
|
82
93
|
await wrapped.__em.populate(this.entity, options.populate, options);
|
|
83
94
|
}
|
|
@@ -137,7 +148,7 @@ export class Reference {
|
|
|
137
148
|
/** @ignore */
|
|
138
149
|
[inspect.custom](depth = 2) {
|
|
139
150
|
const object = { ...this };
|
|
140
|
-
const hidden = ['meta'];
|
|
151
|
+
const hidden = ['meta', 'property'];
|
|
141
152
|
hidden.forEach(k => delete object[k]);
|
|
142
153
|
const ret = inspect(object, { depth });
|
|
143
154
|
const wrapped = helper(this.entity);
|
|
@@ -171,6 +182,22 @@ export class ScalarReference {
|
|
|
171
182
|
}
|
|
172
183
|
return this.value;
|
|
173
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
187
|
+
* Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
|
|
188
|
+
*/
|
|
189
|
+
async loadOrFail(options = {}) {
|
|
190
|
+
const ret = await this.load(options);
|
|
191
|
+
if (ret == null) {
|
|
192
|
+
const wrapped = helper(this.entity);
|
|
193
|
+
options.failHandler ??= wrapped.__em.config.get('findOneOrFailHandler');
|
|
194
|
+
const entityName = this.entity.constructor.name;
|
|
195
|
+
const where = wrapped.getPrimaryKey();
|
|
196
|
+
const whereString = typeof where === 'object' ? inspect(where) : where;
|
|
197
|
+
throw new NotFoundError(`${entityName} (${whereString}) failed to load property '${this.property}'`);
|
|
198
|
+
}
|
|
199
|
+
return ret;
|
|
200
|
+
}
|
|
174
201
|
set(value) {
|
|
175
202
|
this.value = value;
|
|
176
203
|
this.initialized = true;
|
package/entity/WrappedEntity.js
CHANGED
|
@@ -150,7 +150,7 @@ export class WrappedEntity {
|
|
|
150
150
|
return this.__em?.config ?? this.entity.__config;
|
|
151
151
|
}
|
|
152
152
|
get __primaryKeys() {
|
|
153
|
-
return Utils.getPrimaryKeyValues(this.entity, this.__meta
|
|
153
|
+
return Utils.getPrimaryKeyValues(this.entity, this.__meta);
|
|
154
154
|
}
|
|
155
155
|
/** @ignore */
|
|
156
156
|
[inspect.custom]() {
|