| 254 | |
| 255 | /** Wrapper for lazy scalar properties that provides on-demand loading from the database. */ |
| 256 | export class ScalarReference<Value> { |
| 257 | private entity?: object; |
| 258 | #property?: string; |
| 259 | #initialized: boolean; |
| 260 | |
| 261 | constructor( |
| 262 | private value?: Value, |
| 263 | initialized = value != null, |
| 264 | ) { |
| 265 | Object.defineProperty(this, scalarReferenceSymbol, { value: true, enumerable: false }); |
| 266 | this.#initialized = initialized; |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Ensures the underlying entity is loaded first (without reloading it if it already is loaded). |
| 271 | * Returns either the whole entity, or the requested property. |
| 272 | */ |
| 273 | async load( |
| 274 | options?: Omit<LoadReferenceOptions<any, any>, 'populate' | 'fields' | 'exclude'>, |
| 275 | ): Promise<Value | undefined> { |
| 276 | const opts: Dictionary = |
| 277 | typeof options === 'object' ? options : ({ prop: options } as LoadReferenceOptions<any, any>); |
| 278 | |
| 279 | if (!this.#initialized || opts.refresh) { |
| 280 | if (this.entity == null || this.#property == null) { |
| 281 | throw new Error('Cannot load scalar reference that is not bound to an entity property.'); |
| 282 | } |
| 283 | |
| 284 | await helper(this.entity).populate<any>([this.#property], opts); |
| 285 | } |
| 286 | |
| 287 | return this.value; |
| 288 | } |
| 289 | |
| 290 | /** |
| 291 | * Ensures the underlying entity is loaded first (without reloading it if it already is loaded). |
| 292 | * Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options). |
| 293 | */ |
| 294 | async loadOrFail( |
| 295 | options: Omit<LoadReferenceOrFailOptions<any, any>, 'populate' | 'fields' | 'exclude'> = {}, |
| 296 | ): Promise<Value> { |
| 297 | const ret = await this.load(options); |
| 298 | |
| 299 | if (ret == null) { |
| 300 | const wrapped = helper(this.entity!); |
| 301 | options.failHandler ??= wrapped.__em!.config.get('findOneOrFailHandler'); |
| 302 | const entityName = this.entity!.constructor.name; |
| 303 | throw NotFoundError.failedToLoadProperty(entityName, this.#property!, wrapped.getPrimaryKey()); |
| 304 | } |
| 305 | |
| 306 | return ret; |
| 307 | } |
| 308 | |
| 309 | /** Sets the scalar value and marks the reference as initialized. */ |
| 310 | set(value: Value): void { |
| 311 | this.value = value; |
| 312 | this.#initialized = true; |
| 313 | } |
nothing calls this directly
no outgoing calls
no test coverage detected