arkormx 1.2.1 → 1.3.0

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/dist/index.cjs CHANGED
@@ -3449,6 +3449,30 @@ var Relation = class {
3449
3449
  if (results instanceof ArkormCollection) return results.all()[0] ?? null;
3450
3450
  return results;
3451
3451
  }
3452
+ /**
3453
+ * Count records that match the relationship query.
3454
+ *
3455
+ * @returns
3456
+ */
3457
+ async count() {
3458
+ return (await this.getQuery()).count();
3459
+ }
3460
+ /**
3461
+ * Determine whether the relationship query has any matching records.
3462
+ *
3463
+ * @returns
3464
+ */
3465
+ async exists() {
3466
+ return (await this.getQuery()).exists();
3467
+ }
3468
+ /**
3469
+ * Determine whether the relationship query has no matching records.
3470
+ *
3471
+ * @returns
3472
+ */
3473
+ async doesntExist() {
3474
+ return !await this.exists();
3475
+ }
3452
3476
  };
3453
3477
 
3454
3478
  //#endregion
@@ -3471,15 +3495,55 @@ var BelongsToManyRelation = class extends Relation {
3471
3495
  this.relatedKey = relatedKey;
3472
3496
  }
3473
3497
  /**
3498
+ * Build the relationship query.
3499
+ *
3500
+ * @returns
3501
+ */
3502
+ async getQuery() {
3503
+ const parentValue = this.parent.getAttribute(this.parentKey);
3504
+ const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.foreignPivotKey]: parentValue } })).map((row) => row[this.relatedPivotKey]);
3505
+ return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } }));
3506
+ }
3507
+ /**
3474
3508
  * Fetches the related models for this relationship.
3475
3509
  *
3476
3510
  * @returns
3477
3511
  */
3478
3512
  async getResults() {
3479
- const parentValue = this.parent.getAttribute(this.parentKey);
3480
- const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.foreignPivotKey]: parentValue } })).map((row) => row[this.relatedPivotKey]);
3481
- if (ids.length === 0) return new ArkormCollection([]);
3482
- return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
3513
+ return (await this.getQuery()).get();
3514
+ }
3515
+ };
3516
+
3517
+ //#endregion
3518
+ //#region src/relationship/SingleResultRelation.ts
3519
+ /**
3520
+ * Base class for relationships that resolve to a single related model.
3521
+ *
3522
+ * @author Legacy (3m1n3nc3)
3523
+ * @since 1.3.0
3524
+ */
3525
+ var SingleResultRelation = class extends Relation {
3526
+ defaultValue;
3527
+ constructor(parent, related) {
3528
+ super();
3529
+ this.parent = parent;
3530
+ this.related = related;
3531
+ }
3532
+ /**
3533
+ * Defines a default value to return when the relationship does not find a related model.
3534
+ *
3535
+ * @param value The default value or a callback that returns the default value.
3536
+ * @returns The current instance for method chaining.
3537
+ */
3538
+ withDefault(value = {}) {
3539
+ this.defaultValue = value;
3540
+ return this;
3541
+ }
3542
+ resolveDefaultResult() {
3543
+ if (typeof this.defaultValue === "undefined") return null;
3544
+ const resolved = typeof this.defaultValue === "function" ? this.defaultValue(this.parent) : this.defaultValue;
3545
+ if (resolved instanceof this.related) return resolved;
3546
+ return this.related.hydrate(resolved);
3483
3547
  }
3484
3548
  };
3485
3549
 
@@ -3491,22 +3555,28 @@ var BelongsToManyRelation = class extends Relation {
3491
3555
  * @author Legacy (3m1n3nc3)
3492
3556
  * @since 0.1.0
3493
3557
  */
3494
- var BelongsToRelation = class extends Relation {
3558
+ var BelongsToRelation = class extends SingleResultRelation {
3495
3559
  constructor(parent, related, foreignKey, ownerKey) {
3496
- super();
3497
- this.parent = parent;
3498
- this.related = related;
3560
+ super(parent, related);
3499
3561
  this.foreignKey = foreignKey;
3500
3562
  this.ownerKey = ownerKey;
3501
3563
  }
3502
3564
  /**
3565
+ * Build the relationship query.
3566
+ *
3567
+ * @returns
3568
+ */
3569
+ async getQuery() {
3570
+ const foreignValue = this.parent.getAttribute(this.foreignKey);
3571
+ return this.applyConstraint(this.related.query().where({ [this.ownerKey]: foreignValue }));
3572
+ }
3573
+ /**
3503
3574
  * Fetches the related models for this relationship.
3504
3575
  *
3505
3576
  * @returns
3506
3577
  */
3507
3578
  async getResults() {
3508
- const foreignValue = this.parent.getAttribute(this.foreignKey);
3509
- return this.applyConstraint(this.related.query().where({ [this.ownerKey]: foreignValue })).first();
3579
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3510
3580
  }
3511
3581
  };
3512
3582
 
@@ -3527,13 +3597,21 @@ var HasManyRelation = class extends Relation {
3527
3597
  this.localKey = localKey;
3528
3598
  }
3529
3599
  /**
3600
+ * Build the relationship query.
3601
+ *
3602
+ * @returns
3603
+ */
3604
+ async getQuery() {
3605
+ const localValue = this.parent.getAttribute(this.localKey);
3606
+ return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue }));
3607
+ }
3608
+ /**
3530
3609
  * Fetches the related models for this relationship.
3531
3610
  *
3532
3611
  * @returns
3533
3612
  */
3534
3613
  async getResults() {
3535
- const localValue = this.parent.getAttribute(this.localKey);
3536
- return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue })).get();
3614
+ return (await this.getQuery()).get();
3537
3615
  }
3538
3616
  };
3539
3617
 
@@ -3558,15 +3636,22 @@ var HasManyThroughRelation = class extends Relation {
3558
3636
  this.secondLocalKey = secondLocalKey;
3559
3637
  }
3560
3638
  /**
3639
+ * Build the relationship query.
3640
+ *
3641
+ * @returns
3642
+ */
3643
+ async getQuery() {
3644
+ const localValue = this.parent.getAttribute(this.localKey);
3645
+ const keys = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.firstKey]: localValue } })).map((row) => row[this.secondLocalKey]);
3646
+ return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } }));
3647
+ }
3648
+ /**
3561
3649
  * Fetches the related models for this relationship.
3562
3650
  *
3563
3651
  * @returns
3564
3652
  */
3565
3653
  async getResults() {
3566
- const localValue = this.parent.getAttribute(this.localKey);
3567
- const keys = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.firstKey]: localValue } })).map((row) => row[this.secondLocalKey]);
3568
- if (keys.length === 0) return new ArkormCollection([]);
3569
- return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } })).get();
3654
+ return (await this.getQuery()).get();
3570
3655
  }
3571
3656
  };
3572
3657
 
@@ -3578,22 +3663,28 @@ var HasManyThroughRelation = class extends Relation {
3578
3663
  * @author Legacy (3m1n3nc3)
3579
3664
  * @since 0.1.0
3580
3665
  */
3581
- var HasOneRelation = class extends Relation {
3666
+ var HasOneRelation = class extends SingleResultRelation {
3582
3667
  constructor(parent, related, foreignKey, localKey) {
3583
- super();
3584
- this.parent = parent;
3585
- this.related = related;
3668
+ super(parent, related);
3586
3669
  this.foreignKey = foreignKey;
3587
3670
  this.localKey = localKey;
3588
3671
  }
3589
3672
  /**
3673
+ * Build the relationship query.
3674
+ *
3675
+ * @returns
3676
+ */
3677
+ async getQuery() {
3678
+ const localValue = this.parent.getAttribute(this.localKey);
3679
+ return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue }));
3680
+ }
3681
+ /**
3590
3682
  * Fetches the related models for this relationship.
3591
3683
  *
3592
3684
  * @returns
3593
3685
  */
3594
3686
  async getResults() {
3595
- const localValue = this.parent.getAttribute(this.localKey);
3596
- return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue })).first();
3687
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3597
3688
  }
3598
3689
  };
3599
3690
 
@@ -3606,11 +3697,9 @@ var HasOneRelation = class extends Relation {
3606
3697
  * @author Legacy (3m1n3nc3)
3607
3698
  * @since 0.1.0
3608
3699
  */
3609
- var HasOneThroughRelation = class extends Relation {
3700
+ var HasOneThroughRelation = class extends SingleResultRelation {
3610
3701
  constructor(parent, related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey) {
3611
- super();
3612
- this.parent = parent;
3613
- this.related = related;
3702
+ super(parent, related);
3614
3703
  this.throughDelegate = throughDelegate;
3615
3704
  this.firstKey = firstKey;
3616
3705
  this.secondKey = secondKey;
@@ -3618,15 +3707,23 @@ var HasOneThroughRelation = class extends Relation {
3618
3707
  this.secondLocalKey = secondLocalKey;
3619
3708
  }
3620
3709
  /**
3710
+ * Build the relationship query.
3711
+ *
3712
+ * @returns
3713
+ */
3714
+ async getQuery() {
3715
+ const localValue = this.parent.getAttribute(this.localKey);
3716
+ const intermediate = await this.related.getDelegate(this.throughDelegate).findFirst({ where: { [this.firstKey]: localValue } });
3717
+ if (!intermediate) return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: [] } }));
3718
+ return this.applyConstraint(this.related.query().where({ [this.secondKey]: intermediate[this.secondLocalKey] }));
3719
+ }
3720
+ /**
3621
3721
  * Fetches the related models for this relationship.
3622
3722
  *
3623
3723
  * @returns
3624
3724
  */
3625
3725
  async getResults() {
3626
- const localValue = this.parent.getAttribute(this.localKey);
3627
- const intermediate = await this.related.getDelegate(this.throughDelegate).findFirst({ where: { [this.firstKey]: localValue } });
3628
- if (!intermediate) return null;
3629
- return this.applyConstraint(this.related.query().where({ [this.secondKey]: intermediate[this.secondLocalKey] })).first();
3726
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3630
3727
  }
3631
3728
  };
3632
3729
 
@@ -3647,17 +3744,25 @@ var MorphManyRelation = class extends Relation {
3647
3744
  this.localKey = localKey;
3648
3745
  }
3649
3746
  /**
3650
- * Fetches the related models for this relationship.
3651
- *
3652
- * @returns
3747
+ * Build the relationship query.
3748
+ *
3749
+ * @returns
3653
3750
  */
3654
- async getResults() {
3751
+ async getQuery() {
3655
3752
  const id = this.parent.getAttribute(this.localKey);
3656
3753
  const type = this.parent.constructor.name;
3657
3754
  return this.applyConstraint(this.related.query().where({
3658
3755
  [`${this.morphName}Id`]: id,
3659
3756
  [`${this.morphName}Type`]: type
3660
- })).get();
3757
+ }));
3758
+ }
3759
+ /**
3760
+ * Fetches the related models for this relationship.
3761
+ *
3762
+ * @returns
3763
+ */
3764
+ async getResults() {
3765
+ return (await this.getQuery()).get();
3661
3766
  }
3662
3767
  };
3663
3768
 
@@ -3669,26 +3774,32 @@ var MorphManyRelation = class extends Relation {
3669
3774
  * @author Legacy (3m1n3nc3)
3670
3775
  * @since 0.1.0
3671
3776
  */
3672
- var MorphOneRelation = class extends Relation {
3777
+ var MorphOneRelation = class extends SingleResultRelation {
3673
3778
  constructor(parent, related, morphName, localKey) {
3674
- super();
3675
- this.parent = parent;
3676
- this.related = related;
3779
+ super(parent, related);
3677
3780
  this.morphName = morphName;
3678
3781
  this.localKey = localKey;
3679
3782
  }
3680
3783
  /**
3681
- * Fetches the related models for this relationship.
3682
- *
3683
- * @returns
3784
+ * Build the relationship query.
3785
+ *
3786
+ * @returns
3684
3787
  */
3685
- async getResults() {
3788
+ async getQuery() {
3686
3789
  const id = this.parent.getAttribute(this.localKey);
3687
3790
  const type = this.parent.constructor.name;
3688
3791
  return this.applyConstraint(this.related.query().where({
3689
3792
  [`${this.morphName}Id`]: id,
3690
3793
  [`${this.morphName}Type`]: type
3691
- })).first();
3794
+ }));
3795
+ }
3796
+ /**
3797
+ * Fetches the related models for this relationship.
3798
+ *
3799
+ * @returns
3800
+ */
3801
+ async getResults() {
3802
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3692
3803
  }
3693
3804
  };
3694
3805
 
@@ -3712,19 +3823,26 @@ var MorphToManyRelation = class extends Relation {
3712
3823
  this.relatedKey = relatedKey;
3713
3824
  }
3714
3825
  /**
3715
- * Fetches the related models for this relationship.
3716
- *
3717
- * @returns
3826
+ * Build the relationship query.
3827
+ *
3828
+ * @returns
3718
3829
  */
3719
- async getResults() {
3830
+ async getQuery() {
3720
3831
  const parentValue = this.parent.getAttribute(this.parentKey);
3721
3832
  const morphType = this.parent.constructor.name;
3722
3833
  const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: {
3723
3834
  [`${this.morphName}Id`]: parentValue,
3724
3835
  [`${this.morphName}Type`]: morphType
3725
3836
  } })).map((row) => row[this.relatedPivotKey]);
3726
- if (ids.length === 0) return new ArkormCollection([]);
3727
- return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
3837
+ return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } }));
3838
+ }
3839
+ /**
3840
+ * Fetches the related models for this relationship.
3841
+ *
3842
+ * @returns
3843
+ */
3844
+ async getResults() {
3845
+ return (await this.getQuery()).get();
3728
3846
  }
3729
3847
  };
3730
3848
 
package/dist/index.d.cts CHANGED
@@ -118,6 +118,7 @@ declare class ArkormCollection<T = any, X = T[]> extends Collection<T, X> {}
118
118
  //#endregion
119
119
  //#region src/types/relationship.d.ts
120
120
  type RelationConstraint<TModel> = (query: QueryBuilder<TModel>) => QueryBuilder<TModel> | void;
121
+ type RelationDefaultValue<TParent, TRelated> = Partial<ModelAttributes<TRelated>> | TRelated | ((parent: TParent) => Partial<ModelAttributes<TRelated>> | TRelated);
121
122
  //#endregion
122
123
  //#region src/relationship/Relation.d.ts
123
124
  /**
@@ -234,6 +235,12 @@ declare abstract class Relation<TModel> {
234
235
  * @returns The query builder instance with the constraint applied, if any.
235
236
  */
236
237
  protected applyConstraint(query: QueryBuilder<TModel>): QueryBuilder<TModel>;
238
+ /**
239
+ * Build the underlying query for the relationship.
240
+ *
241
+ * @returns
242
+ */
243
+ abstract getQuery(): Promise<QueryBuilder<TModel>>;
237
244
  /**
238
245
  * Execute the relationship query and return relation results.
239
246
  *
@@ -246,6 +253,24 @@ declare abstract class Relation<TModel> {
246
253
  * @returns
247
254
  */
248
255
  first(): Promise<TModel | null>;
256
+ /**
257
+ * Count records that match the relationship query.
258
+ *
259
+ * @returns
260
+ */
261
+ count(): Promise<number>;
262
+ /**
263
+ * Determine whether the relationship query has any matching records.
264
+ *
265
+ * @returns
266
+ */
267
+ exists(): Promise<boolean>;
268
+ /**
269
+ * Determine whether the relationship query has no matching records.
270
+ *
271
+ * @returns
272
+ */
273
+ doesntExist(): Promise<boolean>;
249
274
  /**
250
275
  * Get the results of the relationship query.
251
276
  *
@@ -272,6 +297,12 @@ declare class BelongsToManyRelation<TParent, TRelated> extends Relation<TRelated
272
297
  constructor(parent: TParent & {
273
298
  getAttribute: (key: string) => unknown;
274
299
  }, related: RelationshipModelStatic, throughDelegate: string, foreignPivotKey: string, relatedPivotKey: string, parentKey: string, relatedKey: string);
300
+ /**
301
+ * Build the relationship query.
302
+ *
303
+ * @returns
304
+ */
305
+ getQuery(): Promise<QueryBuilder<TRelated>>;
275
306
  /**
276
307
  * Fetches the related models for this relationship.
277
308
  *
@@ -280,6 +311,28 @@ declare class BelongsToManyRelation<TParent, TRelated> extends Relation<TRelated
280
311
  getResults(): Promise<ArkormCollection<TRelated>>;
281
312
  }
282
313
  //#endregion
314
+ //#region src/relationship/SingleResultRelation.d.ts
315
+ /**
316
+ * Base class for relationships that resolve to a single related model.
317
+ *
318
+ * @author Legacy (3m1n3nc3)
319
+ * @since 1.3.0
320
+ */
321
+ declare abstract class SingleResultRelation<TParent, TRelated> extends Relation<TRelated> {
322
+ protected readonly parent: TParent;
323
+ protected readonly related: RelatedModelClass<TRelated>;
324
+ protected defaultValue: RelationDefaultValue<object, TRelated> | undefined;
325
+ protected constructor(parent: TParent, related: RelatedModelClass<TRelated>);
326
+ /**
327
+ * Defines a default value to return when the relationship does not find a related model.
328
+ *
329
+ * @param value The default value or a callback that returns the default value.
330
+ * @returns The current instance for method chaining.
331
+ */
332
+ withDefault(value?: RelationDefaultValue<TParent, TRelated>): this;
333
+ protected resolveDefaultResult(): TRelated | null;
334
+ }
335
+ //#endregion
283
336
  //#region src/relationship/BelongsToRelation.d.ts
284
337
  /**
285
338
  * Defines an inverse one-to-one or many relationship.
@@ -287,14 +340,20 @@ declare class BelongsToManyRelation<TParent, TRelated> extends Relation<TRelated
287
340
  * @author Legacy (3m1n3nc3)
288
341
  * @since 0.1.0
289
342
  */
290
- declare class BelongsToRelation<TParent, TRelated> extends Relation<TRelated> {
291
- private readonly parent;
292
- private readonly related;
343
+ declare class BelongsToRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
344
+ getAttribute: (key: string) => unknown;
345
+ }, TRelated> {
293
346
  private readonly foreignKey;
294
347
  private readonly ownerKey;
295
348
  constructor(parent: TParent & {
296
349
  getAttribute: (key: string) => unknown;
297
- }, related: RelationshipModelStatic, foreignKey: string, ownerKey: string);
350
+ }, related: RelatedModelClass<TRelated>, foreignKey: string, ownerKey: string);
351
+ /**
352
+ * Build the relationship query.
353
+ *
354
+ * @returns
355
+ */
356
+ getQuery(): Promise<QueryBuilder<TRelated>>;
298
357
  /**
299
358
  * Fetches the related models for this relationship.
300
359
  *
@@ -318,6 +377,12 @@ declare class HasManyRelation<TParent, TRelated> extends Relation<TRelated> {
318
377
  constructor(parent: TParent & {
319
378
  getAttribute: (key: string) => unknown;
320
379
  }, related: RelationshipModelStatic, foreignKey: string, localKey: string);
380
+ /**
381
+ * Build the relationship query.
382
+ *
383
+ * @returns
384
+ */
385
+ getQuery(): Promise<QueryBuilder<TRelated>>;
321
386
  /**
322
387
  * Fetches the related models for this relationship.
323
388
  *
@@ -345,6 +410,12 @@ declare class HasManyThroughRelation<TParent, TRelated> extends Relation<TRelate
345
410
  constructor(parent: TParent & {
346
411
  getAttribute: (key: string) => unknown;
347
412
  }, related: RelationshipModelStatic, throughDelegate: string, firstKey: string, secondKey: string, localKey: string, secondLocalKey: string);
413
+ /**
414
+ * Build the relationship query.
415
+ *
416
+ * @returns
417
+ */
418
+ getQuery(): Promise<QueryBuilder<TRelated>>;
348
419
  /**
349
420
  * Fetches the related models for this relationship.
350
421
  *
@@ -360,14 +431,20 @@ declare class HasManyThroughRelation<TParent, TRelated> extends Relation<TRelate
360
431
  * @author Legacy (3m1n3nc3)
361
432
  * @since 0.1.0
362
433
  */
363
- declare class HasOneRelation<TParent, TRelated> extends Relation<TRelated> {
364
- private readonly parent;
365
- private readonly related;
434
+ declare class HasOneRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
435
+ getAttribute: (key: string) => unknown;
436
+ }, TRelated> {
366
437
  private readonly foreignKey;
367
438
  private readonly localKey;
368
439
  constructor(parent: TParent & {
369
440
  getAttribute: (key: string) => unknown;
370
- }, related: RelationshipModelStatic, foreignKey: string, localKey: string);
441
+ }, related: RelatedModelClass<TRelated>, foreignKey: string, localKey: string);
442
+ /**
443
+ * Build the relationship query.
444
+ *
445
+ * @returns
446
+ */
447
+ getQuery(): Promise<QueryBuilder<TRelated>>;
371
448
  /**
372
449
  * Fetches the related models for this relationship.
373
450
  *
@@ -384,9 +461,9 @@ declare class HasOneRelation<TParent, TRelated> extends Relation<TRelated> {
384
461
  * @author Legacy (3m1n3nc3)
385
462
  * @since 0.1.0
386
463
  */
387
- declare class HasOneThroughRelation<TParent, TRelated> extends Relation<TRelated> {
388
- private readonly parent;
389
- private readonly related;
464
+ declare class HasOneThroughRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
465
+ getAttribute: (key: string) => unknown;
466
+ }, TRelated> {
390
467
  private readonly throughDelegate;
391
468
  private readonly firstKey;
392
469
  private readonly secondKey;
@@ -394,7 +471,13 @@ declare class HasOneThroughRelation<TParent, TRelated> extends Relation<TRelated
394
471
  private readonly secondLocalKey;
395
472
  constructor(parent: TParent & {
396
473
  getAttribute: (key: string) => unknown;
397
- }, related: RelationshipModelStatic, throughDelegate: string, firstKey: string, secondKey: string, localKey: string, secondLocalKey: string);
474
+ }, related: RelatedModelClass<TRelated>, throughDelegate: string, firstKey: string, secondKey: string, localKey: string, secondLocalKey: string);
475
+ /**
476
+ * Build the relationship query.
477
+ *
478
+ * @returns
479
+ */
480
+ getQuery(): Promise<QueryBuilder<TRelated>>;
398
481
  /**
399
482
  * Fetches the related models for this relationship.
400
483
  *
@@ -418,6 +501,12 @@ declare class MorphManyRelation<TParent, TRelated> extends Relation<TRelated> {
418
501
  constructor(parent: TParent & {
419
502
  getAttribute: (key: string) => unknown;
420
503
  }, related: RelationshipModelStatic, morphName: string, localKey: string);
504
+ /**
505
+ * Build the relationship query.
506
+ *
507
+ * @returns
508
+ */
509
+ getQuery(): Promise<QueryBuilder<TRelated>>;
421
510
  /**
422
511
  * Fetches the related models for this relationship.
423
512
  *
@@ -433,14 +522,20 @@ declare class MorphManyRelation<TParent, TRelated> extends Relation<TRelated> {
433
522
  * @author Legacy (3m1n3nc3)
434
523
  * @since 0.1.0
435
524
  */
436
- declare class MorphOneRelation<TParent, TRelated> extends Relation<TRelated> {
437
- private readonly parent;
438
- private readonly related;
525
+ declare class MorphOneRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
526
+ getAttribute: (key: string) => unknown;
527
+ }, TRelated> {
439
528
  private readonly morphName;
440
529
  private readonly localKey;
441
530
  constructor(parent: TParent & {
442
531
  getAttribute: (key: string) => unknown;
443
- }, related: RelationshipModelStatic, morphName: string, localKey: string);
532
+ }, related: RelatedModelClass<TRelated>, morphName: string, localKey: string);
533
+ /**
534
+ * Build the relationship query.
535
+ *
536
+ * @returns
537
+ */
538
+ getQuery(): Promise<QueryBuilder<TRelated>>;
444
539
  /**
445
540
  * Fetches the related models for this relationship.
446
541
  *
@@ -467,6 +562,12 @@ declare class MorphToManyRelation<TParent, TRelated> extends Relation<TRelated>
467
562
  constructor(parent: TParent & {
468
563
  getAttribute: (key: string) => unknown;
469
564
  }, related: RelationshipModelStatic, throughDelegate: string, morphName: string, relatedPivotKey: string, parentKey: string, relatedKey: string);
565
+ /**
566
+ * Build the relationship query.
567
+ *
568
+ * @returns
569
+ */
570
+ getQuery(): Promise<QueryBuilder<TRelated>>;
470
571
  /**
471
572
  * Fetches the related models for this relationship.
472
573
  *
@@ -2016,7 +2117,9 @@ interface ModelStatic<TModel, TDelegate extends PrismaDelegateLike = PrismaDeleg
2016
2117
  getSoftDeleteConfig: () => SoftDeleteConfig;
2017
2118
  }
2018
2119
  interface RelationshipModelStatic {
2120
+ new (attributes?: Record<string, unknown>): any;
2019
2121
  query: () => QueryBuilder<any, any>;
2122
+ hydrate: (attributes: Record<string, unknown>) => any;
2020
2123
  getDelegate: (delegate?: string) => PrismaDelegateLike;
2021
2124
  }
2022
2125
  //#endregion
package/dist/index.d.mts CHANGED
@@ -118,6 +118,7 @@ declare class ArkormCollection<T = any, X = T[]> extends Collection<T, X> {}
118
118
  //#endregion
119
119
  //#region src/types/relationship.d.ts
120
120
  type RelationConstraint<TModel> = (query: QueryBuilder<TModel>) => QueryBuilder<TModel> | void;
121
+ type RelationDefaultValue<TParent, TRelated> = Partial<ModelAttributes<TRelated>> | TRelated | ((parent: TParent) => Partial<ModelAttributes<TRelated>> | TRelated);
121
122
  //#endregion
122
123
  //#region src/relationship/Relation.d.ts
123
124
  /**
@@ -234,6 +235,12 @@ declare abstract class Relation<TModel> {
234
235
  * @returns The query builder instance with the constraint applied, if any.
235
236
  */
236
237
  protected applyConstraint(query: QueryBuilder<TModel>): QueryBuilder<TModel>;
238
+ /**
239
+ * Build the underlying query for the relationship.
240
+ *
241
+ * @returns
242
+ */
243
+ abstract getQuery(): Promise<QueryBuilder<TModel>>;
237
244
  /**
238
245
  * Execute the relationship query and return relation results.
239
246
  *
@@ -246,6 +253,24 @@ declare abstract class Relation<TModel> {
246
253
  * @returns
247
254
  */
248
255
  first(): Promise<TModel | null>;
256
+ /**
257
+ * Count records that match the relationship query.
258
+ *
259
+ * @returns
260
+ */
261
+ count(): Promise<number>;
262
+ /**
263
+ * Determine whether the relationship query has any matching records.
264
+ *
265
+ * @returns
266
+ */
267
+ exists(): Promise<boolean>;
268
+ /**
269
+ * Determine whether the relationship query has no matching records.
270
+ *
271
+ * @returns
272
+ */
273
+ doesntExist(): Promise<boolean>;
249
274
  /**
250
275
  * Get the results of the relationship query.
251
276
  *
@@ -272,6 +297,12 @@ declare class BelongsToManyRelation<TParent, TRelated> extends Relation<TRelated
272
297
  constructor(parent: TParent & {
273
298
  getAttribute: (key: string) => unknown;
274
299
  }, related: RelationshipModelStatic, throughDelegate: string, foreignPivotKey: string, relatedPivotKey: string, parentKey: string, relatedKey: string);
300
+ /**
301
+ * Build the relationship query.
302
+ *
303
+ * @returns
304
+ */
305
+ getQuery(): Promise<QueryBuilder<TRelated>>;
275
306
  /**
276
307
  * Fetches the related models for this relationship.
277
308
  *
@@ -280,6 +311,28 @@ declare class BelongsToManyRelation<TParent, TRelated> extends Relation<TRelated
280
311
  getResults(): Promise<ArkormCollection<TRelated>>;
281
312
  }
282
313
  //#endregion
314
+ //#region src/relationship/SingleResultRelation.d.ts
315
+ /**
316
+ * Base class for relationships that resolve to a single related model.
317
+ *
318
+ * @author Legacy (3m1n3nc3)
319
+ * @since 1.3.0
320
+ */
321
+ declare abstract class SingleResultRelation<TParent, TRelated> extends Relation<TRelated> {
322
+ protected readonly parent: TParent;
323
+ protected readonly related: RelatedModelClass<TRelated>;
324
+ protected defaultValue: RelationDefaultValue<object, TRelated> | undefined;
325
+ protected constructor(parent: TParent, related: RelatedModelClass<TRelated>);
326
+ /**
327
+ * Defines a default value to return when the relationship does not find a related model.
328
+ *
329
+ * @param value The default value or a callback that returns the default value.
330
+ * @returns The current instance for method chaining.
331
+ */
332
+ withDefault(value?: RelationDefaultValue<TParent, TRelated>): this;
333
+ protected resolveDefaultResult(): TRelated | null;
334
+ }
335
+ //#endregion
283
336
  //#region src/relationship/BelongsToRelation.d.ts
284
337
  /**
285
338
  * Defines an inverse one-to-one or many relationship.
@@ -287,14 +340,20 @@ declare class BelongsToManyRelation<TParent, TRelated> extends Relation<TRelated
287
340
  * @author Legacy (3m1n3nc3)
288
341
  * @since 0.1.0
289
342
  */
290
- declare class BelongsToRelation<TParent, TRelated> extends Relation<TRelated> {
291
- private readonly parent;
292
- private readonly related;
343
+ declare class BelongsToRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
344
+ getAttribute: (key: string) => unknown;
345
+ }, TRelated> {
293
346
  private readonly foreignKey;
294
347
  private readonly ownerKey;
295
348
  constructor(parent: TParent & {
296
349
  getAttribute: (key: string) => unknown;
297
- }, related: RelationshipModelStatic, foreignKey: string, ownerKey: string);
350
+ }, related: RelatedModelClass<TRelated>, foreignKey: string, ownerKey: string);
351
+ /**
352
+ * Build the relationship query.
353
+ *
354
+ * @returns
355
+ */
356
+ getQuery(): Promise<QueryBuilder<TRelated>>;
298
357
  /**
299
358
  * Fetches the related models for this relationship.
300
359
  *
@@ -318,6 +377,12 @@ declare class HasManyRelation<TParent, TRelated> extends Relation<TRelated> {
318
377
  constructor(parent: TParent & {
319
378
  getAttribute: (key: string) => unknown;
320
379
  }, related: RelationshipModelStatic, foreignKey: string, localKey: string);
380
+ /**
381
+ * Build the relationship query.
382
+ *
383
+ * @returns
384
+ */
385
+ getQuery(): Promise<QueryBuilder<TRelated>>;
321
386
  /**
322
387
  * Fetches the related models for this relationship.
323
388
  *
@@ -345,6 +410,12 @@ declare class HasManyThroughRelation<TParent, TRelated> extends Relation<TRelate
345
410
  constructor(parent: TParent & {
346
411
  getAttribute: (key: string) => unknown;
347
412
  }, related: RelationshipModelStatic, throughDelegate: string, firstKey: string, secondKey: string, localKey: string, secondLocalKey: string);
413
+ /**
414
+ * Build the relationship query.
415
+ *
416
+ * @returns
417
+ */
418
+ getQuery(): Promise<QueryBuilder<TRelated>>;
348
419
  /**
349
420
  * Fetches the related models for this relationship.
350
421
  *
@@ -360,14 +431,20 @@ declare class HasManyThroughRelation<TParent, TRelated> extends Relation<TRelate
360
431
  * @author Legacy (3m1n3nc3)
361
432
  * @since 0.1.0
362
433
  */
363
- declare class HasOneRelation<TParent, TRelated> extends Relation<TRelated> {
364
- private readonly parent;
365
- private readonly related;
434
+ declare class HasOneRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
435
+ getAttribute: (key: string) => unknown;
436
+ }, TRelated> {
366
437
  private readonly foreignKey;
367
438
  private readonly localKey;
368
439
  constructor(parent: TParent & {
369
440
  getAttribute: (key: string) => unknown;
370
- }, related: RelationshipModelStatic, foreignKey: string, localKey: string);
441
+ }, related: RelatedModelClass<TRelated>, foreignKey: string, localKey: string);
442
+ /**
443
+ * Build the relationship query.
444
+ *
445
+ * @returns
446
+ */
447
+ getQuery(): Promise<QueryBuilder<TRelated>>;
371
448
  /**
372
449
  * Fetches the related models for this relationship.
373
450
  *
@@ -384,9 +461,9 @@ declare class HasOneRelation<TParent, TRelated> extends Relation<TRelated> {
384
461
  * @author Legacy (3m1n3nc3)
385
462
  * @since 0.1.0
386
463
  */
387
- declare class HasOneThroughRelation<TParent, TRelated> extends Relation<TRelated> {
388
- private readonly parent;
389
- private readonly related;
464
+ declare class HasOneThroughRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
465
+ getAttribute: (key: string) => unknown;
466
+ }, TRelated> {
390
467
  private readonly throughDelegate;
391
468
  private readonly firstKey;
392
469
  private readonly secondKey;
@@ -394,7 +471,13 @@ declare class HasOneThroughRelation<TParent, TRelated> extends Relation<TRelated
394
471
  private readonly secondLocalKey;
395
472
  constructor(parent: TParent & {
396
473
  getAttribute: (key: string) => unknown;
397
- }, related: RelationshipModelStatic, throughDelegate: string, firstKey: string, secondKey: string, localKey: string, secondLocalKey: string);
474
+ }, related: RelatedModelClass<TRelated>, throughDelegate: string, firstKey: string, secondKey: string, localKey: string, secondLocalKey: string);
475
+ /**
476
+ * Build the relationship query.
477
+ *
478
+ * @returns
479
+ */
480
+ getQuery(): Promise<QueryBuilder<TRelated>>;
398
481
  /**
399
482
  * Fetches the related models for this relationship.
400
483
  *
@@ -418,6 +501,12 @@ declare class MorphManyRelation<TParent, TRelated> extends Relation<TRelated> {
418
501
  constructor(parent: TParent & {
419
502
  getAttribute: (key: string) => unknown;
420
503
  }, related: RelationshipModelStatic, morphName: string, localKey: string);
504
+ /**
505
+ * Build the relationship query.
506
+ *
507
+ * @returns
508
+ */
509
+ getQuery(): Promise<QueryBuilder<TRelated>>;
421
510
  /**
422
511
  * Fetches the related models for this relationship.
423
512
  *
@@ -433,14 +522,20 @@ declare class MorphManyRelation<TParent, TRelated> extends Relation<TRelated> {
433
522
  * @author Legacy (3m1n3nc3)
434
523
  * @since 0.1.0
435
524
  */
436
- declare class MorphOneRelation<TParent, TRelated> extends Relation<TRelated> {
437
- private readonly parent;
438
- private readonly related;
525
+ declare class MorphOneRelation<TParent, TRelated> extends SingleResultRelation<TParent & {
526
+ getAttribute: (key: string) => unknown;
527
+ }, TRelated> {
439
528
  private readonly morphName;
440
529
  private readonly localKey;
441
530
  constructor(parent: TParent & {
442
531
  getAttribute: (key: string) => unknown;
443
- }, related: RelationshipModelStatic, morphName: string, localKey: string);
532
+ }, related: RelatedModelClass<TRelated>, morphName: string, localKey: string);
533
+ /**
534
+ * Build the relationship query.
535
+ *
536
+ * @returns
537
+ */
538
+ getQuery(): Promise<QueryBuilder<TRelated>>;
444
539
  /**
445
540
  * Fetches the related models for this relationship.
446
541
  *
@@ -467,6 +562,12 @@ declare class MorphToManyRelation<TParent, TRelated> extends Relation<TRelated>
467
562
  constructor(parent: TParent & {
468
563
  getAttribute: (key: string) => unknown;
469
564
  }, related: RelationshipModelStatic, throughDelegate: string, morphName: string, relatedPivotKey: string, parentKey: string, relatedKey: string);
565
+ /**
566
+ * Build the relationship query.
567
+ *
568
+ * @returns
569
+ */
570
+ getQuery(): Promise<QueryBuilder<TRelated>>;
470
571
  /**
471
572
  * Fetches the related models for this relationship.
472
573
  *
@@ -2016,7 +2117,9 @@ interface ModelStatic<TModel, TDelegate extends PrismaDelegateLike = PrismaDeleg
2016
2117
  getSoftDeleteConfig: () => SoftDeleteConfig;
2017
2118
  }
2018
2119
  interface RelationshipModelStatic {
2120
+ new (attributes?: Record<string, unknown>): any;
2019
2121
  query: () => QueryBuilder<any, any>;
2122
+ hydrate: (attributes: Record<string, unknown>) => any;
2020
2123
  getDelegate: (delegate?: string) => PrismaDelegateLike;
2021
2124
  }
2022
2125
  //#endregion
package/dist/index.mjs CHANGED
@@ -3420,6 +3420,30 @@ var Relation = class {
3420
3420
  if (results instanceof ArkormCollection) return results.all()[0] ?? null;
3421
3421
  return results;
3422
3422
  }
3423
+ /**
3424
+ * Count records that match the relationship query.
3425
+ *
3426
+ * @returns
3427
+ */
3428
+ async count() {
3429
+ return (await this.getQuery()).count();
3430
+ }
3431
+ /**
3432
+ * Determine whether the relationship query has any matching records.
3433
+ *
3434
+ * @returns
3435
+ */
3436
+ async exists() {
3437
+ return (await this.getQuery()).exists();
3438
+ }
3439
+ /**
3440
+ * Determine whether the relationship query has no matching records.
3441
+ *
3442
+ * @returns
3443
+ */
3444
+ async doesntExist() {
3445
+ return !await this.exists();
3446
+ }
3423
3447
  };
3424
3448
 
3425
3449
  //#endregion
@@ -3442,15 +3466,55 @@ var BelongsToManyRelation = class extends Relation {
3442
3466
  this.relatedKey = relatedKey;
3443
3467
  }
3444
3468
  /**
3469
+ * Build the relationship query.
3470
+ *
3471
+ * @returns
3472
+ */
3473
+ async getQuery() {
3474
+ const parentValue = this.parent.getAttribute(this.parentKey);
3475
+ const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.foreignPivotKey]: parentValue } })).map((row) => row[this.relatedPivotKey]);
3476
+ return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } }));
3477
+ }
3478
+ /**
3445
3479
  * Fetches the related models for this relationship.
3446
3480
  *
3447
3481
  * @returns
3448
3482
  */
3449
3483
  async getResults() {
3450
- const parentValue = this.parent.getAttribute(this.parentKey);
3451
- const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.foreignPivotKey]: parentValue } })).map((row) => row[this.relatedPivotKey]);
3452
- if (ids.length === 0) return new ArkormCollection([]);
3453
- return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
3484
+ return (await this.getQuery()).get();
3485
+ }
3486
+ };
3487
+
3488
+ //#endregion
3489
+ //#region src/relationship/SingleResultRelation.ts
3490
+ /**
3491
+ * Base class for relationships that resolve to a single related model.
3492
+ *
3493
+ * @author Legacy (3m1n3nc3)
3494
+ * @since 1.3.0
3495
+ */
3496
+ var SingleResultRelation = class extends Relation {
3497
+ defaultValue;
3498
+ constructor(parent, related) {
3499
+ super();
3500
+ this.parent = parent;
3501
+ this.related = related;
3502
+ }
3503
+ /**
3504
+ * Defines a default value to return when the relationship does not find a related model.
3505
+ *
3506
+ * @param value The default value or a callback that returns the default value.
3507
+ * @returns The current instance for method chaining.
3508
+ */
3509
+ withDefault(value = {}) {
3510
+ this.defaultValue = value;
3511
+ return this;
3512
+ }
3513
+ resolveDefaultResult() {
3514
+ if (typeof this.defaultValue === "undefined") return null;
3515
+ const resolved = typeof this.defaultValue === "function" ? this.defaultValue(this.parent) : this.defaultValue;
3516
+ if (resolved instanceof this.related) return resolved;
3517
+ return this.related.hydrate(resolved);
3454
3518
  }
3455
3519
  };
3456
3520
 
@@ -3462,22 +3526,28 @@ var BelongsToManyRelation = class extends Relation {
3462
3526
  * @author Legacy (3m1n3nc3)
3463
3527
  * @since 0.1.0
3464
3528
  */
3465
- var BelongsToRelation = class extends Relation {
3529
+ var BelongsToRelation = class extends SingleResultRelation {
3466
3530
  constructor(parent, related, foreignKey, ownerKey) {
3467
- super();
3468
- this.parent = parent;
3469
- this.related = related;
3531
+ super(parent, related);
3470
3532
  this.foreignKey = foreignKey;
3471
3533
  this.ownerKey = ownerKey;
3472
3534
  }
3473
3535
  /**
3536
+ * Build the relationship query.
3537
+ *
3538
+ * @returns
3539
+ */
3540
+ async getQuery() {
3541
+ const foreignValue = this.parent.getAttribute(this.foreignKey);
3542
+ return this.applyConstraint(this.related.query().where({ [this.ownerKey]: foreignValue }));
3543
+ }
3544
+ /**
3474
3545
  * Fetches the related models for this relationship.
3475
3546
  *
3476
3547
  * @returns
3477
3548
  */
3478
3549
  async getResults() {
3479
- const foreignValue = this.parent.getAttribute(this.foreignKey);
3480
- return this.applyConstraint(this.related.query().where({ [this.ownerKey]: foreignValue })).first();
3550
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3481
3551
  }
3482
3552
  };
3483
3553
 
@@ -3498,13 +3568,21 @@ var HasManyRelation = class extends Relation {
3498
3568
  this.localKey = localKey;
3499
3569
  }
3500
3570
  /**
3571
+ * Build the relationship query.
3572
+ *
3573
+ * @returns
3574
+ */
3575
+ async getQuery() {
3576
+ const localValue = this.parent.getAttribute(this.localKey);
3577
+ return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue }));
3578
+ }
3579
+ /**
3501
3580
  * Fetches the related models for this relationship.
3502
3581
  *
3503
3582
  * @returns
3504
3583
  */
3505
3584
  async getResults() {
3506
- const localValue = this.parent.getAttribute(this.localKey);
3507
- return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue })).get();
3585
+ return (await this.getQuery()).get();
3508
3586
  }
3509
3587
  };
3510
3588
 
@@ -3529,15 +3607,22 @@ var HasManyThroughRelation = class extends Relation {
3529
3607
  this.secondLocalKey = secondLocalKey;
3530
3608
  }
3531
3609
  /**
3610
+ * Build the relationship query.
3611
+ *
3612
+ * @returns
3613
+ */
3614
+ async getQuery() {
3615
+ const localValue = this.parent.getAttribute(this.localKey);
3616
+ const keys = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.firstKey]: localValue } })).map((row) => row[this.secondLocalKey]);
3617
+ return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } }));
3618
+ }
3619
+ /**
3532
3620
  * Fetches the related models for this relationship.
3533
3621
  *
3534
3622
  * @returns
3535
3623
  */
3536
3624
  async getResults() {
3537
- const localValue = this.parent.getAttribute(this.localKey);
3538
- const keys = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.firstKey]: localValue } })).map((row) => row[this.secondLocalKey]);
3539
- if (keys.length === 0) return new ArkormCollection([]);
3540
- return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } })).get();
3625
+ return (await this.getQuery()).get();
3541
3626
  }
3542
3627
  };
3543
3628
 
@@ -3549,22 +3634,28 @@ var HasManyThroughRelation = class extends Relation {
3549
3634
  * @author Legacy (3m1n3nc3)
3550
3635
  * @since 0.1.0
3551
3636
  */
3552
- var HasOneRelation = class extends Relation {
3637
+ var HasOneRelation = class extends SingleResultRelation {
3553
3638
  constructor(parent, related, foreignKey, localKey) {
3554
- super();
3555
- this.parent = parent;
3556
- this.related = related;
3639
+ super(parent, related);
3557
3640
  this.foreignKey = foreignKey;
3558
3641
  this.localKey = localKey;
3559
3642
  }
3560
3643
  /**
3644
+ * Build the relationship query.
3645
+ *
3646
+ * @returns
3647
+ */
3648
+ async getQuery() {
3649
+ const localValue = this.parent.getAttribute(this.localKey);
3650
+ return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue }));
3651
+ }
3652
+ /**
3561
3653
  * Fetches the related models for this relationship.
3562
3654
  *
3563
3655
  * @returns
3564
3656
  */
3565
3657
  async getResults() {
3566
- const localValue = this.parent.getAttribute(this.localKey);
3567
- return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue })).first();
3658
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3568
3659
  }
3569
3660
  };
3570
3661
 
@@ -3577,11 +3668,9 @@ var HasOneRelation = class extends Relation {
3577
3668
  * @author Legacy (3m1n3nc3)
3578
3669
  * @since 0.1.0
3579
3670
  */
3580
- var HasOneThroughRelation = class extends Relation {
3671
+ var HasOneThroughRelation = class extends SingleResultRelation {
3581
3672
  constructor(parent, related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey) {
3582
- super();
3583
- this.parent = parent;
3584
- this.related = related;
3673
+ super(parent, related);
3585
3674
  this.throughDelegate = throughDelegate;
3586
3675
  this.firstKey = firstKey;
3587
3676
  this.secondKey = secondKey;
@@ -3589,15 +3678,23 @@ var HasOneThroughRelation = class extends Relation {
3589
3678
  this.secondLocalKey = secondLocalKey;
3590
3679
  }
3591
3680
  /**
3681
+ * Build the relationship query.
3682
+ *
3683
+ * @returns
3684
+ */
3685
+ async getQuery() {
3686
+ const localValue = this.parent.getAttribute(this.localKey);
3687
+ const intermediate = await this.related.getDelegate(this.throughDelegate).findFirst({ where: { [this.firstKey]: localValue } });
3688
+ if (!intermediate) return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: [] } }));
3689
+ return this.applyConstraint(this.related.query().where({ [this.secondKey]: intermediate[this.secondLocalKey] }));
3690
+ }
3691
+ /**
3592
3692
  * Fetches the related models for this relationship.
3593
3693
  *
3594
3694
  * @returns
3595
3695
  */
3596
3696
  async getResults() {
3597
- const localValue = this.parent.getAttribute(this.localKey);
3598
- const intermediate = await this.related.getDelegate(this.throughDelegate).findFirst({ where: { [this.firstKey]: localValue } });
3599
- if (!intermediate) return null;
3600
- return this.applyConstraint(this.related.query().where({ [this.secondKey]: intermediate[this.secondLocalKey] })).first();
3697
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3601
3698
  }
3602
3699
  };
3603
3700
 
@@ -3618,17 +3715,25 @@ var MorphManyRelation = class extends Relation {
3618
3715
  this.localKey = localKey;
3619
3716
  }
3620
3717
  /**
3621
- * Fetches the related models for this relationship.
3622
- *
3623
- * @returns
3718
+ * Build the relationship query.
3719
+ *
3720
+ * @returns
3624
3721
  */
3625
- async getResults() {
3722
+ async getQuery() {
3626
3723
  const id = this.parent.getAttribute(this.localKey);
3627
3724
  const type = this.parent.constructor.name;
3628
3725
  return this.applyConstraint(this.related.query().where({
3629
3726
  [`${this.morphName}Id`]: id,
3630
3727
  [`${this.morphName}Type`]: type
3631
- })).get();
3728
+ }));
3729
+ }
3730
+ /**
3731
+ * Fetches the related models for this relationship.
3732
+ *
3733
+ * @returns
3734
+ */
3735
+ async getResults() {
3736
+ return (await this.getQuery()).get();
3632
3737
  }
3633
3738
  };
3634
3739
 
@@ -3640,26 +3745,32 @@ var MorphManyRelation = class extends Relation {
3640
3745
  * @author Legacy (3m1n3nc3)
3641
3746
  * @since 0.1.0
3642
3747
  */
3643
- var MorphOneRelation = class extends Relation {
3748
+ var MorphOneRelation = class extends SingleResultRelation {
3644
3749
  constructor(parent, related, morphName, localKey) {
3645
- super();
3646
- this.parent = parent;
3647
- this.related = related;
3750
+ super(parent, related);
3648
3751
  this.morphName = morphName;
3649
3752
  this.localKey = localKey;
3650
3753
  }
3651
3754
  /**
3652
- * Fetches the related models for this relationship.
3653
- *
3654
- * @returns
3755
+ * Build the relationship query.
3756
+ *
3757
+ * @returns
3655
3758
  */
3656
- async getResults() {
3759
+ async getQuery() {
3657
3760
  const id = this.parent.getAttribute(this.localKey);
3658
3761
  const type = this.parent.constructor.name;
3659
3762
  return this.applyConstraint(this.related.query().where({
3660
3763
  [`${this.morphName}Id`]: id,
3661
3764
  [`${this.morphName}Type`]: type
3662
- })).first();
3765
+ }));
3766
+ }
3767
+ /**
3768
+ * Fetches the related models for this relationship.
3769
+ *
3770
+ * @returns
3771
+ */
3772
+ async getResults() {
3773
+ return await (await this.getQuery()).first() ?? this.resolveDefaultResult();
3663
3774
  }
3664
3775
  };
3665
3776
 
@@ -3683,19 +3794,26 @@ var MorphToManyRelation = class extends Relation {
3683
3794
  this.relatedKey = relatedKey;
3684
3795
  }
3685
3796
  /**
3686
- * Fetches the related models for this relationship.
3687
- *
3688
- * @returns
3797
+ * Build the relationship query.
3798
+ *
3799
+ * @returns
3689
3800
  */
3690
- async getResults() {
3801
+ async getQuery() {
3691
3802
  const parentValue = this.parent.getAttribute(this.parentKey);
3692
3803
  const morphType = this.parent.constructor.name;
3693
3804
  const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: {
3694
3805
  [`${this.morphName}Id`]: parentValue,
3695
3806
  [`${this.morphName}Type`]: morphType
3696
3807
  } })).map((row) => row[this.relatedPivotKey]);
3697
- if (ids.length === 0) return new ArkormCollection([]);
3698
- return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
3808
+ return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } }));
3809
+ }
3810
+ /**
3811
+ * Fetches the related models for this relationship.
3812
+ *
3813
+ * @returns
3814
+ */
3815
+ async getResults() {
3816
+ return (await this.getQuery()).get();
3699
3817
  }
3700
3818
  };
3701
3819
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkormx",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Modern TypeScript-first ORM for Node.js.",
5
5
  "keywords": [
6
6
  "orm",