@bunyad/orm 0.1.0-alpha.0 → 0.2.0-beta.1
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/README.md +25 -3
- package/dist/concerns.d.ts +2 -2
- package/dist/factory.d.ts +3 -3
- package/dist/model-events.d.ts +2 -2
- package/dist/model-events.js +1 -1
- package/dist/model-query.d.ts +65 -59
- package/dist/model-query.js +94 -57
- package/dist/model-strictness.d.ts +2 -3
- package/dist/model-strictness.js +2 -3
- package/dist/model.d.ts +115 -107
- package/dist/model.js +120 -104
- package/dist/orm-collection.d.ts +1 -1
- package/dist/orm-collection.js +1 -1
- package/dist/relations.d.ts +4 -4
- package/dist/relations.js +3 -3
- package/dist/scopes.d.ts +1 -2
- package/dist/scopes.js +1 -2
- package/package.json +3 -3
package/dist/model.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DatabaseManager as Db, setDefaultConnection, connections, LengthAwarePaginator, CursorPaginator, Paginator, dateTimeForStorage, withoutQueryWriteHook, wrapSqlName, } from "@bunyad/database";
|
|
2
|
-
import { Collection } from "@bunyad/common";
|
|
2
|
+
import { Collection, LazyCollection } from "@bunyad/common";
|
|
3
3
|
import { OrmCollection } from "./orm-collection.js";
|
|
4
4
|
import { Attribute, castFromStorage, castToStorage, hashCastValue, isAlreadyHashed, enumFromStorage, enumToStorage, isAttribute, isCastType, isEnumCast, } from "./casts.js";
|
|
5
5
|
import { bootIfNotBooted, fireModelEvent, hasAnyModelEventListeners, hasModelEventListeners, observeModel, registerModelEvent, withoutModelEvents, } from "./model-events.js";
|
|
@@ -355,7 +355,7 @@ export class Model {
|
|
|
355
355
|
/** When false, `save()` skips `created_at` / `updated_at`. */
|
|
356
356
|
static timestamps = true;
|
|
357
357
|
/**
|
|
358
|
-
*
|
|
358
|
+
* `$casts` property — prefer `static casts()` when adding enums / Attribute.
|
|
359
359
|
* May be a plain object or a method returning the map.
|
|
360
360
|
*/
|
|
361
361
|
static casts = {};
|
|
@@ -366,19 +366,19 @@ export class Model {
|
|
|
366
366
|
*/
|
|
367
367
|
static relations = {};
|
|
368
368
|
/**
|
|
369
|
-
*
|
|
369
|
+
* `$touches` — relation names whose related models get `touch()` after
|
|
370
370
|
* this model is saved (or soft-deleted). Honor `withoutTouching`.
|
|
371
371
|
*/
|
|
372
372
|
static touches = [];
|
|
373
|
-
/**
|
|
373
|
+
/** `$hidden`. */
|
|
374
374
|
static hidden = [];
|
|
375
|
-
/**
|
|
375
|
+
/** `$visible` allow-list (empty = all non-hidden). */
|
|
376
376
|
static visible = [];
|
|
377
|
-
/**
|
|
377
|
+
/** `$appends` — accessors included in `toArray()`. */
|
|
378
378
|
static appends = [];
|
|
379
|
-
/**
|
|
379
|
+
/** `$fillable`. */
|
|
380
380
|
static fillable = [];
|
|
381
|
-
/**
|
|
381
|
+
/** `$guarded` — default guard all until fillable is set. */
|
|
382
382
|
static guarded = ["*"];
|
|
383
383
|
/** Set in constructor / hydrate — avoid empty `{}` alloc before assign. */
|
|
384
384
|
#original;
|
|
@@ -395,7 +395,7 @@ export class Model {
|
|
|
395
395
|
#visible = null;
|
|
396
396
|
/** Per-instance connection override (`$model->setConnection()`). */
|
|
397
397
|
#connection;
|
|
398
|
-
/**
|
|
398
|
+
/** `$exists` — true after retrieve / successful insert. */
|
|
399
399
|
get exists() {
|
|
400
400
|
return this.#exists;
|
|
401
401
|
}
|
|
@@ -444,7 +444,7 @@ export class Model {
|
|
|
444
444
|
return out;
|
|
445
445
|
}
|
|
446
446
|
/**
|
|
447
|
-
*
|
|
447
|
+
* `getAttribute` — throws when `preventAccessingMissingAttributes` is on
|
|
448
448
|
* and the key was not retrieved / does not exist (relations exempt).
|
|
449
449
|
*/
|
|
450
450
|
getAttribute(key) {
|
|
@@ -478,18 +478,18 @@ export class Model {
|
|
|
478
478
|
}
|
|
479
479
|
return undefined;
|
|
480
480
|
}
|
|
481
|
-
/**
|
|
481
|
+
/** `isDirty`. */
|
|
482
482
|
isDirty(...attributes) {
|
|
483
483
|
const dirty = this.getDirty();
|
|
484
484
|
if (attributes.length === 0)
|
|
485
485
|
return Object.keys(dirty).length > 0;
|
|
486
486
|
return attributes.some((key) => Object.prototype.hasOwnProperty.call(dirty, key));
|
|
487
487
|
}
|
|
488
|
-
/**
|
|
488
|
+
/** `isClean`. */
|
|
489
489
|
isClean(...attributes) {
|
|
490
490
|
return !this.isDirty(...attributes);
|
|
491
491
|
}
|
|
492
|
-
/**
|
|
492
|
+
/** `getDirty`. */
|
|
493
493
|
getDirty() {
|
|
494
494
|
const dirty = {};
|
|
495
495
|
const original = this.#original ?? {};
|
|
@@ -505,11 +505,11 @@ export class Model {
|
|
|
505
505
|
return { ...original };
|
|
506
506
|
return original[key];
|
|
507
507
|
}
|
|
508
|
-
/**
|
|
508
|
+
/** `getChanges` — attributes changed by the last save. */
|
|
509
509
|
getChanges() {
|
|
510
510
|
return this.#changes ? { ...this.#changes } : {};
|
|
511
511
|
}
|
|
512
|
-
/**
|
|
512
|
+
/** `wasChanged`. */
|
|
513
513
|
wasChanged(...attributes) {
|
|
514
514
|
const changes = this.#changes;
|
|
515
515
|
if (!changes)
|
|
@@ -518,50 +518,50 @@ export class Model {
|
|
|
518
518
|
return Object.keys(changes).length > 0;
|
|
519
519
|
return attributes.some((key) => Object.prototype.hasOwnProperty.call(changes, key));
|
|
520
520
|
}
|
|
521
|
-
/**
|
|
521
|
+
/** `wasRecentlyCreated`. */
|
|
522
522
|
wasRecentlyCreated() {
|
|
523
523
|
return this.#wasRecentlyCreated;
|
|
524
524
|
}
|
|
525
|
-
/**
|
|
525
|
+
/** `append`. */
|
|
526
526
|
append(...attributes) {
|
|
527
527
|
const current = this.getAppends();
|
|
528
528
|
this.#appends = [...new Set([...current, ...attributes.flat()])];
|
|
529
529
|
return this;
|
|
530
530
|
}
|
|
531
|
-
/**
|
|
531
|
+
/** `setAppends`. */
|
|
532
532
|
setAppends(attributes) {
|
|
533
533
|
this.#appends = [...attributes];
|
|
534
534
|
return this;
|
|
535
535
|
}
|
|
536
|
-
/**
|
|
536
|
+
/** `getAppends`. */
|
|
537
537
|
getAppends() {
|
|
538
538
|
if (this.#appends)
|
|
539
539
|
return [...this.#appends];
|
|
540
540
|
return [...(this.constructor.appends ?? [])];
|
|
541
541
|
}
|
|
542
|
-
/**
|
|
542
|
+
/** `getHidden`. */
|
|
543
543
|
getHidden() {
|
|
544
544
|
if (this.#hidden)
|
|
545
545
|
return [...this.#hidden];
|
|
546
546
|
return [...(this.constructor.hidden ?? [])];
|
|
547
547
|
}
|
|
548
|
-
/**
|
|
548
|
+
/** `getVisible`. */
|
|
549
549
|
getVisible() {
|
|
550
550
|
if (this.#visible)
|
|
551
551
|
return [...this.#visible];
|
|
552
552
|
return [...(this.constructor.visible ?? [])];
|
|
553
553
|
}
|
|
554
|
-
/**
|
|
554
|
+
/** `setHidden`. */
|
|
555
555
|
setHidden(attributes) {
|
|
556
556
|
this.#hidden = [...attributes];
|
|
557
557
|
return this;
|
|
558
558
|
}
|
|
559
|
-
/**
|
|
559
|
+
/** `setVisible`. */
|
|
560
560
|
setVisible(attributes) {
|
|
561
561
|
this.#visible = [...attributes];
|
|
562
562
|
return this;
|
|
563
563
|
}
|
|
564
|
-
/**
|
|
564
|
+
/** `makeVisible` — show attributes for this instance. */
|
|
565
565
|
makeVisible(...attributes) {
|
|
566
566
|
const attrs = attributes.flat();
|
|
567
567
|
const hidden = new Set(this.getHidden());
|
|
@@ -574,7 +574,7 @@ export class Model {
|
|
|
574
574
|
}
|
|
575
575
|
return this;
|
|
576
576
|
}
|
|
577
|
-
/**
|
|
577
|
+
/** `makeHidden` — hide attributes for this instance. */
|
|
578
578
|
makeHidden(...attributes) {
|
|
579
579
|
const attrs = attributes.flat();
|
|
580
580
|
this.#hidden = [...new Set([...this.getHidden(), ...attrs])];
|
|
@@ -586,7 +586,7 @@ export class Model {
|
|
|
586
586
|
return this;
|
|
587
587
|
}
|
|
588
588
|
/**
|
|
589
|
-
* Run a callback without touching timestamps (
|
|
589
|
+
* Run a callback without touching timestamps (`Model::withoutTimestamps`).
|
|
590
590
|
*/
|
|
591
591
|
static async withoutTimestamps(callback) {
|
|
592
592
|
const previous = this.timestamps;
|
|
@@ -605,23 +605,23 @@ export class Model {
|
|
|
605
605
|
static isIgnoringTouch() {
|
|
606
606
|
return isIgnoringTouch(this);
|
|
607
607
|
}
|
|
608
|
-
/**
|
|
608
|
+
/** `Model::preventLazyLoading`. */
|
|
609
609
|
static preventLazyLoading(prevent = true) {
|
|
610
610
|
setPreventLazyLoading(prevent);
|
|
611
611
|
}
|
|
612
|
-
/**
|
|
612
|
+
/** `Model::preventSilentlyDiscardingAttributes`. */
|
|
613
613
|
static preventSilentlyDiscardingAttributes(prevent = true) {
|
|
614
614
|
setPreventSilentlyDiscardingAttributes(prevent);
|
|
615
615
|
}
|
|
616
|
-
/**
|
|
616
|
+
/** `Model::preventAccessingMissingAttributes`. */
|
|
617
617
|
static preventAccessingMissingAttributes(prevent = true) {
|
|
618
618
|
setPreventAccessingMissingAttributes(prevent);
|
|
619
619
|
}
|
|
620
|
-
/**
|
|
620
|
+
/** `Model::shouldBeStrict` — enable/disable the strictness trio. */
|
|
621
621
|
static shouldBeStrict(should = true) {
|
|
622
622
|
setShouldBeStrict(should);
|
|
623
623
|
}
|
|
624
|
-
/**
|
|
624
|
+
/** `Model::addGlobalScope`. */
|
|
625
625
|
static addGlobalScope(nameOrScope, scope) {
|
|
626
626
|
if (typeof nameOrScope === "function") {
|
|
627
627
|
registerGlobalScope(this, nameOrScope.name || `scope_${Date.now()}`, nameOrScope);
|
|
@@ -631,28 +631,28 @@ export class Model {
|
|
|
631
631
|
throw new Error("addGlobalScope requires a callback.");
|
|
632
632
|
registerGlobalScope(this, nameOrScope, scope);
|
|
633
633
|
}
|
|
634
|
-
/**
|
|
634
|
+
/** `Model::addGlobalScope` removal helper. */
|
|
635
635
|
static removeGlobalScope(name) {
|
|
636
636
|
unregisterGlobalScope(this, name);
|
|
637
637
|
}
|
|
638
638
|
/**
|
|
639
|
-
*
|
|
639
|
+
* `boot` — runs once per model class before first use.
|
|
640
640
|
* Prefer registering listeners in `booted()`.
|
|
641
641
|
*/
|
|
642
642
|
static boot() { }
|
|
643
643
|
/**
|
|
644
|
-
*
|
|
644
|
+
* `booted` — runs once after `boot`. Typical place for `static::created(...)`.
|
|
645
645
|
*/
|
|
646
646
|
static booted() { }
|
|
647
647
|
/** Ensure this model class has been booted. */
|
|
648
648
|
static bootIfNotBooted() {
|
|
649
649
|
bootIfNotBooted(this);
|
|
650
650
|
}
|
|
651
|
-
/**
|
|
651
|
+
/** `Model::observe`. */
|
|
652
652
|
static observe(observer) {
|
|
653
653
|
observeModel(this, observer);
|
|
654
654
|
}
|
|
655
|
-
/**
|
|
655
|
+
/** `Model::withoutEvents`. */
|
|
656
656
|
static withoutEvents(callback) {
|
|
657
657
|
return withoutModelEvents(this, callback);
|
|
658
658
|
}
|
|
@@ -779,7 +779,7 @@ export class Model {
|
|
|
779
779
|
static getConnection() {
|
|
780
780
|
return resolveConnection(this.connection);
|
|
781
781
|
}
|
|
782
|
-
/**
|
|
782
|
+
/** `$model->setConnection($name)`. */
|
|
783
783
|
setConnection(connection) {
|
|
784
784
|
this.#connection = connection;
|
|
785
785
|
return this;
|
|
@@ -799,7 +799,7 @@ export class Model {
|
|
|
799
799
|
return manager;
|
|
800
800
|
}
|
|
801
801
|
/**
|
|
802
|
-
*
|
|
802
|
+
* `Model::on($connection)` — same model, different named connection.
|
|
803
803
|
*/
|
|
804
804
|
static on(connection) {
|
|
805
805
|
return this.newQuery({ connection });
|
|
@@ -814,21 +814,21 @@ export class Model {
|
|
|
814
814
|
const query = new Ctor(this, options);
|
|
815
815
|
return withLocalScopeProxy(query);
|
|
816
816
|
}
|
|
817
|
-
/**
|
|
817
|
+
/** `withoutGlobalScopes()`. */
|
|
818
818
|
static withoutGlobalScopes(names) {
|
|
819
819
|
return this.newQuery({
|
|
820
820
|
withoutGlobalScopes: names ?? true,
|
|
821
821
|
});
|
|
822
822
|
}
|
|
823
|
-
/**
|
|
823
|
+
/** `withoutGlobalScope($name)`. */
|
|
824
824
|
static withoutGlobalScope(name) {
|
|
825
825
|
return this.newQuery({ withoutGlobalScopes: [name] });
|
|
826
826
|
}
|
|
827
|
-
/**
|
|
827
|
+
/** `Model::query()` — scoped `ModelQuery` (not a raw table builder). */
|
|
828
828
|
static query(options) {
|
|
829
829
|
return this.newQuery(options);
|
|
830
830
|
}
|
|
831
|
-
/**
|
|
831
|
+
/** `Model::find`. */
|
|
832
832
|
static find(id) {
|
|
833
833
|
this.bootIfNotBooted();
|
|
834
834
|
if (canFastFind(this)) {
|
|
@@ -839,12 +839,12 @@ export class Model {
|
|
|
839
839
|
.where(this.primaryKey, id)
|
|
840
840
|
.first();
|
|
841
841
|
}
|
|
842
|
-
/**
|
|
842
|
+
/** `getRouteKeyName()` — column used for implicit route binding. */
|
|
843
843
|
static getRouteKeyName() {
|
|
844
844
|
return this.primaryKey;
|
|
845
845
|
}
|
|
846
846
|
/**
|
|
847
|
-
*
|
|
847
|
+
* `resolveRouteBinding` — find by PK or custom `{param:column}` field.
|
|
848
848
|
*/
|
|
849
849
|
static resolveRouteBinding(value, field) {
|
|
850
850
|
const column = field ?? this.getRouteKeyName();
|
|
@@ -855,7 +855,7 @@ export class Model {
|
|
|
855
855
|
return this.newQuery().where(column, value).first();
|
|
856
856
|
}
|
|
857
857
|
/**
|
|
858
|
-
*
|
|
858
|
+
* `resolveSoftDeletableRouteBinding` — same as
|
|
859
859
|
* `resolveRouteBinding` but includes soft-deleted rows (`withTrashed()`).
|
|
860
860
|
* Used by the router when the route opts in via `withTrashed()`.
|
|
861
861
|
*/
|
|
@@ -885,20 +885,20 @@ export class Model {
|
|
|
885
885
|
model.#exists = true;
|
|
886
886
|
return model;
|
|
887
887
|
}
|
|
888
|
-
/**
|
|
888
|
+
/** `Model::findOrFail`. */
|
|
889
889
|
static async findOrFail(id) {
|
|
890
890
|
const model = await this.find(id);
|
|
891
891
|
if (!model)
|
|
892
892
|
throw new ModelNotFoundException();
|
|
893
893
|
return model;
|
|
894
894
|
}
|
|
895
|
-
/**
|
|
895
|
+
/** `Model::all`. */
|
|
896
896
|
static async all() {
|
|
897
897
|
this.bootIfNotBooted();
|
|
898
898
|
return this.newQuery().get();
|
|
899
899
|
}
|
|
900
900
|
/**
|
|
901
|
-
*
|
|
901
|
+
* `Model::get` — same as `query().get()` / `all()`.
|
|
902
902
|
* Typed helper for autocomplete; Proxy also forwards other terminals.
|
|
903
903
|
*/
|
|
904
904
|
static get() {
|
|
@@ -995,30 +995,30 @@ export class Model {
|
|
|
995
995
|
return q.whereDate(column, opOrValue);
|
|
996
996
|
return q.whereDate(column, String(opOrValue), value);
|
|
997
997
|
}
|
|
998
|
-
/**
|
|
998
|
+
/** `Model::whereNot(closure)`. */
|
|
999
999
|
static whereNot(callback) {
|
|
1000
1000
|
this.bootIfNotBooted();
|
|
1001
1001
|
return this.newQuery().whereNot(callback);
|
|
1002
1002
|
}
|
|
1003
|
-
/**
|
|
1003
|
+
/** `Model::orWhereNot(closure)`. */
|
|
1004
1004
|
static orWhereNot(callback) {
|
|
1005
1005
|
this.bootIfNotBooted();
|
|
1006
1006
|
return this.newQuery().orWhereNot(callback);
|
|
1007
1007
|
}
|
|
1008
|
-
/**
|
|
1008
|
+
/** `withTrashed()`. */
|
|
1009
1009
|
static withTrashed() {
|
|
1010
1010
|
this.bootIfNotBooted();
|
|
1011
1011
|
return this.newQuery({
|
|
1012
1012
|
withTrashed: true,
|
|
1013
1013
|
});
|
|
1014
1014
|
}
|
|
1015
|
-
/**
|
|
1015
|
+
/** `onlyTrashed()`. */
|
|
1016
1016
|
static onlyTrashed() {
|
|
1017
1017
|
return this.newQuery({
|
|
1018
1018
|
onlyTrashed: true,
|
|
1019
1019
|
});
|
|
1020
1020
|
}
|
|
1021
|
-
/**
|
|
1021
|
+
/** `withoutTrashed()`. */
|
|
1022
1022
|
static withoutTrashed() {
|
|
1023
1023
|
return this.newQuery({
|
|
1024
1024
|
withTrashed: false,
|
|
@@ -1035,27 +1035,27 @@ export class Model {
|
|
|
1035
1035
|
eagerLoad: normalizeWithRelations(relations),
|
|
1036
1036
|
});
|
|
1037
1037
|
}
|
|
1038
|
-
/**
|
|
1038
|
+
/** `Model::whereHas`. */
|
|
1039
1039
|
static whereHas(relation, callback) {
|
|
1040
1040
|
this.bootIfNotBooted();
|
|
1041
1041
|
return this.newQuery().whereHas(relation, callback);
|
|
1042
1042
|
}
|
|
1043
|
-
/**
|
|
1043
|
+
/** `Model::whereDoesntHave`. */
|
|
1044
1044
|
static whereDoesntHave(relation, callback) {
|
|
1045
1045
|
this.bootIfNotBooted();
|
|
1046
1046
|
return this.newQuery().whereDoesntHave(relation, callback);
|
|
1047
1047
|
}
|
|
1048
|
-
/**
|
|
1048
|
+
/** `Model::orWhereHas`. */
|
|
1049
1049
|
static orWhereHas(relation, callback) {
|
|
1050
1050
|
this.bootIfNotBooted();
|
|
1051
1051
|
return this.newQuery().orWhereHas(relation, callback);
|
|
1052
1052
|
}
|
|
1053
|
-
/**
|
|
1053
|
+
/** `Model::whereRelation`. */
|
|
1054
1054
|
static whereRelation(relation, column, opOrValue, value) {
|
|
1055
1055
|
this.bootIfNotBooted();
|
|
1056
1056
|
return this.newQuery().whereRelation(relation, column, opOrValue, value);
|
|
1057
1057
|
}
|
|
1058
|
-
/**
|
|
1058
|
+
/** `Model::orWhereRelation`. */
|
|
1059
1059
|
static orWhereRelation(relation, column, opOrValue, value) {
|
|
1060
1060
|
this.bootIfNotBooted();
|
|
1061
1061
|
return this.newQuery().orWhereRelation(relation, column, opOrValue, value);
|
|
@@ -1065,7 +1065,7 @@ export class Model {
|
|
|
1065
1065
|
this.bootIfNotBooted();
|
|
1066
1066
|
return this.newQuery().withCount(...relations);
|
|
1067
1067
|
}
|
|
1068
|
-
/**
|
|
1068
|
+
/** `Model::withSum`. */
|
|
1069
1069
|
static withSum(relation, column) {
|
|
1070
1070
|
this.bootIfNotBooted();
|
|
1071
1071
|
return this.newQuery().withSum(relation, column);
|
|
@@ -1110,40 +1110,56 @@ export class Model {
|
|
|
1110
1110
|
this.bootIfNotBooted();
|
|
1111
1111
|
return this.newQuery().whereKey(id);
|
|
1112
1112
|
}
|
|
1113
|
-
/**
|
|
1113
|
+
/** `Model::whereUuid`. */
|
|
1114
1114
|
static whereUuid(column, value) {
|
|
1115
1115
|
this.bootIfNotBooted();
|
|
1116
1116
|
return this.newQuery().whereUuid(column, value);
|
|
1117
1117
|
}
|
|
1118
|
-
/**
|
|
1118
|
+
/** `Model::whereUlid`. */
|
|
1119
1119
|
static whereUlid(column, value) {
|
|
1120
1120
|
this.bootIfNotBooted();
|
|
1121
1121
|
return this.newQuery().whereUlid(column, value);
|
|
1122
1122
|
}
|
|
1123
|
-
/**
|
|
1123
|
+
/** `Model::join`. */
|
|
1124
1124
|
static join(table, first, op, second) {
|
|
1125
1125
|
this.bootIfNotBooted();
|
|
1126
1126
|
return this.newQuery().join(table, first, op, second);
|
|
1127
1127
|
}
|
|
1128
|
-
/**
|
|
1128
|
+
/** `Model::leftJoin`. */
|
|
1129
1129
|
static leftJoin(table, first, op, second) {
|
|
1130
1130
|
this.bootIfNotBooted();
|
|
1131
1131
|
return this.newQuery().leftJoin(table, first, op, second);
|
|
1132
1132
|
}
|
|
1133
|
-
/**
|
|
1133
|
+
/** `Model::paginate`. */
|
|
1134
1134
|
static async paginate(perPage = 15, page, options = {}) {
|
|
1135
1135
|
return this.newQuery().paginate(perPage, page, options);
|
|
1136
1136
|
}
|
|
1137
|
-
/**
|
|
1137
|
+
/** `Model::cursorPaginate`. */
|
|
1138
1138
|
static async cursorPaginate(perPage = 15, cursor = null, options = {}) {
|
|
1139
1139
|
return this.newQuery().cursorPaginate(perPage, cursor, options);
|
|
1140
1140
|
}
|
|
1141
|
-
/**
|
|
1141
|
+
/** `Model::cursor()`: stream models from a single query. */
|
|
1142
|
+
static cursor(chunkSize) {
|
|
1143
|
+
return this.newQuery().cursor(chunkSize);
|
|
1144
|
+
}
|
|
1145
|
+
/** `Model::lazy()`: chunked iteration, one query per chunk. */
|
|
1146
|
+
static lazy(chunkSize) {
|
|
1147
|
+
return this.newQuery().lazy(chunkSize);
|
|
1148
|
+
}
|
|
1149
|
+
/** `Model::lazyById()`: chunked iteration by primary key (safe while updating). */
|
|
1150
|
+
static lazyById(chunkSize, column) {
|
|
1151
|
+
return this.newQuery().lazyById(chunkSize, column);
|
|
1152
|
+
}
|
|
1153
|
+
/** `Model::lazyByIdDesc()`. */
|
|
1154
|
+
static lazyByIdDesc(chunkSize, column) {
|
|
1155
|
+
return this.newQuery().lazyByIdDesc(chunkSize, column);
|
|
1156
|
+
}
|
|
1157
|
+
/** `Model::create` (respects `$fillable` / `$guarded`). */
|
|
1142
1158
|
static create(attributes) {
|
|
1143
1159
|
this.bootIfNotBooted();
|
|
1144
1160
|
return this.forceCreate(filterFillable(this, attributes));
|
|
1145
1161
|
}
|
|
1146
|
-
/**
|
|
1162
|
+
/** `Model::forceCreate` — insert every given attribute, ignoring `$fillable`. */
|
|
1147
1163
|
static forceCreate(useAttrs) {
|
|
1148
1164
|
this.bootIfNotBooted();
|
|
1149
1165
|
// Assign raw attributes (skip get-casts) so mutators like Attribute.set can run on save.
|
|
@@ -1176,7 +1192,7 @@ export class Model {
|
|
|
1176
1192
|
}
|
|
1177
1193
|
return after(saved);
|
|
1178
1194
|
}
|
|
1179
|
-
/**
|
|
1195
|
+
/** `firstOrNew`. */
|
|
1180
1196
|
static async firstOrNew(attributes, values = {}) {
|
|
1181
1197
|
let q = this.newQuery();
|
|
1182
1198
|
for (const [column, value] of Object.entries(attributes)) {
|
|
@@ -1187,7 +1203,7 @@ export class Model {
|
|
|
1187
1203
|
return existing;
|
|
1188
1204
|
return new this({ ...attributes, ...values });
|
|
1189
1205
|
}
|
|
1190
|
-
/**
|
|
1206
|
+
/** `firstOrCreate`. */
|
|
1191
1207
|
static async firstOrCreate(attributes, values = {}) {
|
|
1192
1208
|
let q = this.newQuery();
|
|
1193
1209
|
for (const [column, value] of Object.entries(attributes)) {
|
|
@@ -1198,7 +1214,7 @@ export class Model {
|
|
|
1198
1214
|
return existing;
|
|
1199
1215
|
return this.create({ ...attributes, ...values });
|
|
1200
1216
|
}
|
|
1201
|
-
/**
|
|
1217
|
+
/** `updateOrCreate`. */
|
|
1202
1218
|
static async updateOrCreate(attributes, values = {}) {
|
|
1203
1219
|
let q = this.newQuery();
|
|
1204
1220
|
for (const [column, value] of Object.entries(attributes)) {
|
|
@@ -1220,21 +1236,21 @@ export class Model {
|
|
|
1220
1236
|
return this.newQuery().insert(values);
|
|
1221
1237
|
}
|
|
1222
1238
|
/**
|
|
1223
|
-
*
|
|
1239
|
+
* `Model::upsert` — bulk insert-or-update on unique conflict.
|
|
1224
1240
|
* Delegates to `ModelQuery.upsert` (timestamps stamped when enabled).
|
|
1225
1241
|
*/
|
|
1226
1242
|
static async upsert(values, uniqueBy, update = null) {
|
|
1227
1243
|
this.bootIfNotBooted();
|
|
1228
1244
|
return this.newQuery().upsert(values, uniqueBy, update);
|
|
1229
1245
|
}
|
|
1230
|
-
/**
|
|
1246
|
+
/** `$model->fill($attributes)`. */
|
|
1231
1247
|
fill(attributes) {
|
|
1232
1248
|
const ctor = this.constructor;
|
|
1233
1249
|
const finalAttrs = filterFillable(ctor, attributes);
|
|
1234
1250
|
assignOwn(this, ctor.castAttributes(finalAttrs, "get"));
|
|
1235
1251
|
return this;
|
|
1236
1252
|
}
|
|
1237
|
-
/**
|
|
1253
|
+
/** `$model->update($attributes)`. */
|
|
1238
1254
|
async update(attributes) {
|
|
1239
1255
|
this.fill(attributes);
|
|
1240
1256
|
await this.save();
|
|
@@ -1441,16 +1457,16 @@ export class Model {
|
|
|
1441
1457
|
}
|
|
1442
1458
|
this.syncOriginal();
|
|
1443
1459
|
await fireModelEvent(this, "saved");
|
|
1444
|
-
//
|
|
1460
|
+
// finishSave → touchOwners (Relation.touch honors withoutTouching on related).
|
|
1445
1461
|
await this.touchOwners();
|
|
1446
1462
|
return this;
|
|
1447
1463
|
}
|
|
1448
|
-
/**
|
|
1464
|
+
/** `$model->saveQuietly()`. */
|
|
1449
1465
|
async saveQuietly() {
|
|
1450
1466
|
const ctor = this.constructor;
|
|
1451
1467
|
return ctor.withoutEvents(() => this.save());
|
|
1452
1468
|
}
|
|
1453
|
-
/**
|
|
1469
|
+
/** `$model->refresh()`. */
|
|
1454
1470
|
async refresh() {
|
|
1455
1471
|
const ctor = this.constructor;
|
|
1456
1472
|
const key = ctor.primaryKey;
|
|
@@ -1464,7 +1480,7 @@ export class Model {
|
|
|
1464
1480
|
return this;
|
|
1465
1481
|
}
|
|
1466
1482
|
/**
|
|
1467
|
-
*
|
|
1483
|
+
* Refresh under row lock (`lockForUpdate`).
|
|
1468
1484
|
* Reloads this instance from a locked select of the same primary key.
|
|
1469
1485
|
*/
|
|
1470
1486
|
async refreshForUpdate() {
|
|
@@ -1482,7 +1498,7 @@ export class Model {
|
|
|
1482
1498
|
}
|
|
1483
1499
|
return this;
|
|
1484
1500
|
}
|
|
1485
|
-
/**
|
|
1501
|
+
/** `$model->fresh()`. */
|
|
1486
1502
|
async fresh() {
|
|
1487
1503
|
const ctor = this.constructor;
|
|
1488
1504
|
const key = ctor.primaryKey;
|
|
@@ -1502,7 +1518,7 @@ export class Model {
|
|
|
1502
1518
|
await eagerLoadModels([this], missing);
|
|
1503
1519
|
return this;
|
|
1504
1520
|
}
|
|
1505
|
-
/**
|
|
1521
|
+
/** `$model->relationLoaded($key)`. */
|
|
1506
1522
|
relationLoaded(key) {
|
|
1507
1523
|
return Object.prototype.hasOwnProperty.call(this, key);
|
|
1508
1524
|
}
|
|
@@ -1526,14 +1542,14 @@ export class Model {
|
|
|
1526
1542
|
}
|
|
1527
1543
|
}
|
|
1528
1544
|
/**
|
|
1529
|
-
*
|
|
1545
|
+
* `$model->touches($relation)` — whether `$touches` lists this relation.
|
|
1530
1546
|
*/
|
|
1531
1547
|
touches(relation) {
|
|
1532
1548
|
const ctor = this.constructor;
|
|
1533
1549
|
return (ctor.touches ?? []).includes(relation);
|
|
1534
1550
|
}
|
|
1535
1551
|
/**
|
|
1536
|
-
*
|
|
1552
|
+
* `$model->touchOwners()` — touch each relation in `$touches`.
|
|
1537
1553
|
* Uses `relation.touch()` when available (BelongsTo / HasOne / HasMany);
|
|
1538
1554
|
* otherwise loads the related model and calls `touch()` on it.
|
|
1539
1555
|
*/
|
|
@@ -1556,7 +1572,7 @@ export class Model {
|
|
|
1556
1572
|
}
|
|
1557
1573
|
}
|
|
1558
1574
|
/**
|
|
1559
|
-
*
|
|
1575
|
+
* `$model->touch()` — bump `updated_at` (no-op while `withoutTouching`).
|
|
1560
1576
|
* Saving also runs `$touches` / `touchOwners()` for listed relations.
|
|
1561
1577
|
*/
|
|
1562
1578
|
async touch() {
|
|
@@ -1574,7 +1590,7 @@ export class Model {
|
|
|
1574
1590
|
return true;
|
|
1575
1591
|
}
|
|
1576
1592
|
/**
|
|
1577
|
-
* Relation query builder (`$model->product()`
|
|
1593
|
+
* Relation query builder (`$model->product()`).
|
|
1578
1594
|
*
|
|
1579
1595
|
* Define relations on `static relations` and declare the loaded property:
|
|
1580
1596
|
* `declare product: Product | null` — then use `model.product` after `with('product')`,
|
|
@@ -1605,7 +1621,7 @@ export class Model {
|
|
|
1605
1621
|
}
|
|
1606
1622
|
throw new Error(`Call to undefined relationship [${name}] on model [${ctor.name ?? "Model"}].`);
|
|
1607
1623
|
}
|
|
1608
|
-
/**
|
|
1624
|
+
/** `$model->replicate()`. */
|
|
1609
1625
|
replicate(except = []) {
|
|
1610
1626
|
const ctor = this.constructor;
|
|
1611
1627
|
ctor.bootIfNotBooted();
|
|
@@ -1618,7 +1634,7 @@ export class Model {
|
|
|
1618
1634
|
attrs[k] = v;
|
|
1619
1635
|
}
|
|
1620
1636
|
const copy = new ctor(attrs);
|
|
1621
|
-
// Fire sync-style; listeners may be async but replicate is sync
|
|
1637
|
+
// Fire sync-style; listeners may be async but replicate is sync.
|
|
1622
1638
|
void fireModelEvent(copy, "replicating");
|
|
1623
1639
|
return copy;
|
|
1624
1640
|
}
|
|
@@ -1633,7 +1649,7 @@ export class Model {
|
|
|
1633
1649
|
await fireModelEvent(this, "trashed");
|
|
1634
1650
|
return;
|
|
1635
1651
|
}
|
|
1636
|
-
//
|
|
1652
|
+
// delete(): touchOwners before performDeleteOnModel.
|
|
1637
1653
|
await this.touchOwners();
|
|
1638
1654
|
await this.#performForceDelete();
|
|
1639
1655
|
await fireModelEvent(this, "deleted");
|
|
@@ -1664,7 +1680,7 @@ export class Model {
|
|
|
1664
1680
|
await fireModelEvent(this, "forceDeleted");
|
|
1665
1681
|
await fireModelEvent(this, "deleted");
|
|
1666
1682
|
}
|
|
1667
|
-
/**
|
|
1683
|
+
/** `$model->deleteQuietly()`. */
|
|
1668
1684
|
async deleteQuietly() {
|
|
1669
1685
|
const ctor = this.constructor;
|
|
1670
1686
|
await ctor.withoutEvents(() => this.delete());
|
|
@@ -1705,7 +1721,7 @@ export class Model {
|
|
|
1705
1721
|
const value = this[col];
|
|
1706
1722
|
return value != null && value !== "";
|
|
1707
1723
|
}
|
|
1708
|
-
/**
|
|
1724
|
+
/** `$model->toArray()`. */
|
|
1709
1725
|
toArray() {
|
|
1710
1726
|
const ctor = this.constructor;
|
|
1711
1727
|
const hiddenList = this.getHidden();
|
|
@@ -1746,7 +1762,7 @@ export class Model {
|
|
|
1746
1762
|
}
|
|
1747
1763
|
return this[key];
|
|
1748
1764
|
}
|
|
1749
|
-
/**
|
|
1765
|
+
/** `$model->loadCount(...$relations)`. */
|
|
1750
1766
|
async loadCount(...relations) {
|
|
1751
1767
|
const names = relations.flat();
|
|
1752
1768
|
if (names.length === 0)
|
|
@@ -1758,7 +1774,7 @@ export class Model {
|
|
|
1758
1774
|
}
|
|
1759
1775
|
return this;
|
|
1760
1776
|
}
|
|
1761
|
-
/**
|
|
1777
|
+
/** `$model->loadSum($relation, $column)`. */
|
|
1762
1778
|
async loadSum(relation, column) {
|
|
1763
1779
|
const parsed = parseRelationAlias(relation);
|
|
1764
1780
|
const value = await aggregateRelation(this, parsed.relation, "sum", column);
|
|
@@ -1783,7 +1799,7 @@ export class Model {
|
|
|
1783
1799
|
this[parsed.alias ?? `${parsed.relation}_max_${column}`] = value;
|
|
1784
1800
|
return this;
|
|
1785
1801
|
}
|
|
1786
|
-
/**
|
|
1802
|
+
/** `$model->loadExists(...$relations)`. */
|
|
1787
1803
|
async loadExists(...relations) {
|
|
1788
1804
|
for (const relation of relations.flat()) {
|
|
1789
1805
|
const parsed = parseRelationAlias(relation);
|
|
@@ -1799,7 +1815,7 @@ export class Model {
|
|
|
1799
1815
|
toJson() {
|
|
1800
1816
|
return this.toJSON();
|
|
1801
1817
|
}
|
|
1802
|
-
/**
|
|
1818
|
+
/** `$model->increment($column, $amount)`. */
|
|
1803
1819
|
async increment(column, amount = 1) {
|
|
1804
1820
|
const row = this;
|
|
1805
1821
|
const current = Number(row[column] ?? 0);
|
|
@@ -1807,11 +1823,11 @@ export class Model {
|
|
|
1807
1823
|
await this.save();
|
|
1808
1824
|
return this;
|
|
1809
1825
|
}
|
|
1810
|
-
/**
|
|
1826
|
+
/** `$model->decrement($column, $amount)`. */
|
|
1811
1827
|
async decrement(column, amount = 1) {
|
|
1812
1828
|
return this.increment(column, -amount);
|
|
1813
1829
|
}
|
|
1814
|
-
/**
|
|
1830
|
+
/** `Model::destroy($ids)`. */
|
|
1815
1831
|
static async destroy(ids) {
|
|
1816
1832
|
const list = Array.isArray(ids) ? ids : [ids];
|
|
1817
1833
|
let n = 0;
|
|
@@ -1825,7 +1841,7 @@ export class Model {
|
|
|
1825
1841
|
return n;
|
|
1826
1842
|
}
|
|
1827
1843
|
/**
|
|
1828
|
-
*
|
|
1844
|
+
* `Model::forceDestroy($ids)` — permanent delete by key(s).
|
|
1829
1845
|
* Soft-delete models are found via `withTrashed()` so trashed rows are included.
|
|
1830
1846
|
*/
|
|
1831
1847
|
static async forceDestroy(ids) {
|
|
@@ -1842,18 +1858,18 @@ export class Model {
|
|
|
1842
1858
|
}
|
|
1843
1859
|
return n;
|
|
1844
1860
|
}
|
|
1845
|
-
/**
|
|
1861
|
+
/** `$this->hasMany(Related::class)`. */
|
|
1846
1862
|
hasMany(related, foreignKey, localKey) {
|
|
1847
1863
|
const key = foreignKey ?? `${singular(this.constructor.table)}_id`;
|
|
1848
1864
|
return new HasMany(this, related, key, localKey);
|
|
1849
1865
|
}
|
|
1850
|
-
/**
|
|
1866
|
+
/** `$this->hasOne(Related::class)`. */
|
|
1851
1867
|
hasOne(related, foreignKey, localKey) {
|
|
1852
1868
|
const key = foreignKey ?? `${singular(this.constructor.table)}_id`;
|
|
1853
1869
|
return new HasOne(this, related, key, localKey);
|
|
1854
1870
|
}
|
|
1855
1871
|
/**
|
|
1856
|
-
*
|
|
1872
|
+
* `$this->hasManyThrough(Related::class, Through::class)`.
|
|
1857
1873
|
* Parent → Through (firstKey) → Related (secondKey).
|
|
1858
1874
|
*/
|
|
1859
1875
|
hasManyThrough(related, through, firstKey, secondKey, localKey, secondLocalKey) {
|
|
@@ -1861,18 +1877,18 @@ export class Model {
|
|
|
1861
1877
|
return new HasManyThrough(this, related, through, firstKey ?? `${singular(parentCtor.table)}_id`, secondKey ?? `${singular(through.table)}_id`, localKey ?? parentCtor.primaryKey, secondLocalKey ?? through.primaryKey);
|
|
1862
1878
|
}
|
|
1863
1879
|
/**
|
|
1864
|
-
*
|
|
1880
|
+
* `$this->hasOneThrough(Related::class, Through::class)`.
|
|
1865
1881
|
*/
|
|
1866
1882
|
hasOneThrough(related, through, firstKey, secondKey, localKey, secondLocalKey) {
|
|
1867
1883
|
const parentCtor = this.constructor;
|
|
1868
1884
|
return new HasOneThrough(this, related, through, firstKey ?? `${singular(parentCtor.table)}_id`, secondKey ?? `${singular(through.table)}_id`, localKey ?? parentCtor.primaryKey, secondLocalKey ?? through.primaryKey);
|
|
1869
1885
|
}
|
|
1870
|
-
/**
|
|
1886
|
+
/** `$this->belongsTo(Related::class)`. */
|
|
1871
1887
|
belongsTo(related, foreignKey, ownerKey) {
|
|
1872
1888
|
const key = foreignKey ?? `${singular(related.table)}_id`;
|
|
1873
1889
|
return new BelongsTo(this, related, key, ownerKey);
|
|
1874
1890
|
}
|
|
1875
|
-
/**
|
|
1891
|
+
/** `$this->belongsToMany(Related::class, pivot)`. */
|
|
1876
1892
|
belongsToMany(related, table, foreignPivotKey, relatedPivotKey) {
|
|
1877
1893
|
const parent = this.constructor;
|
|
1878
1894
|
const pivot = table ??
|
|
@@ -1903,15 +1919,15 @@ export class Model {
|
|
|
1903
1919
|
: [name, primaryMorph].filter((value, index, all) => all.indexOf(value) === index);
|
|
1904
1920
|
return new MorphToMany(this, related, table, foreign, relatedKey, morphTypeColumn, morphTypes, options.pivotTenantKey, options.parentTenantKey ?? options.pivotTenantKey);
|
|
1905
1921
|
}
|
|
1906
|
-
/**
|
|
1922
|
+
/** `$this->morphMany(Related::class, name)`. */
|
|
1907
1923
|
morphMany(related, name, type, id, localKey) {
|
|
1908
1924
|
return new MorphMany(this, related, type ?? `${name}_type`, id ?? `${name}_id`, morphTypeFor(this.constructor), localKey);
|
|
1909
1925
|
}
|
|
1910
|
-
/**
|
|
1926
|
+
/** `$this->morphOne(Related::class, name)`. */
|
|
1911
1927
|
morphOne(related, name, type, id, localKey) {
|
|
1912
1928
|
return new MorphOne(this, related, type ?? `${name}_type`, id ?? `${name}_id`, morphTypeFor(this.constructor), localKey);
|
|
1913
1929
|
}
|
|
1914
|
-
/**
|
|
1930
|
+
/** `$this->morphTo($name)`. */
|
|
1915
1931
|
morphTo(name, type, id, ownerKey) {
|
|
1916
1932
|
return new MorphTo(this, type ?? `${name}_type`, id ?? `${name}_id`, ownerKey);
|
|
1917
1933
|
}
|