@getstrata/core 0.5.98 → 0.5.99
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/CHANGELOG.md +20 -0
- package/dist/core/database/baseRepository.d.ts +3 -1
- package/dist/core/database/factory.d.ts +19 -4
- package/dist/core/database/index.d.ts +1 -1
- package/dist/core/database/model.d.ts +54 -19
- package/dist/core/database/relationQuery.d.ts +24 -1
- package/dist/core/database/repositoryQuery.d.ts +4 -1
- package/dist/core/http/resources.d.ts +2 -1
- package/dist/entries/database/factory.js +44 -2
- package/dist/entries/database/model.js +412 -122
- package/dist/entries/database/query.js +1 -1
- package/dist/entries/database/repositoryQuery.js +29 -3
- package/dist/entries/database/schema.js +1 -1
- package/dist/entries/http/resources.js +9 -3
- package/dist/framework/public-api.d.ts +1 -1
- package/dist/index.js +514 -148
- package/package.json +1 -1
|
@@ -97,7 +97,7 @@ function buildOperatorClauses(column, operator, params) {
|
|
|
97
97
|
clauses.push(`${column} <= ${pushParam(params, operator.lte)}`);
|
|
98
98
|
}
|
|
99
99
|
if (operator.ilike !== undefined) {
|
|
100
|
-
clauses.push(`${column} ILIKE ${pushParam(params,
|
|
100
|
+
clauses.push(`${column} ILIKE ${pushParam(params, operator.ilike)}`);
|
|
101
101
|
}
|
|
102
102
|
if (operator.tsMatch !== undefined) {
|
|
103
103
|
clauses.push(`${column} @@ plainto_tsquery('english', ${pushParam(params, operator.tsMatch)})`);
|
|
@@ -458,6 +458,9 @@ function ownerId(owner, ownerKey) {
|
|
|
458
458
|
}
|
|
459
459
|
throw new Error("belongsTo.associate() requires a related model or { id }.");
|
|
460
460
|
}
|
|
461
|
+
function thenGet(get, onfulfilled, onrejected) {
|
|
462
|
+
return get().then(onfulfilled ?? undefined, onrejected ?? undefined);
|
|
463
|
+
}
|
|
461
464
|
|
|
462
465
|
class HasManyRelationQuery {
|
|
463
466
|
parent;
|
|
@@ -521,13 +524,28 @@ class HasManyRelationQuery {
|
|
|
521
524
|
return rows[0] ?? null;
|
|
522
525
|
}
|
|
523
526
|
async count() {
|
|
524
|
-
return
|
|
527
|
+
return this.scopedQuery().count();
|
|
528
|
+
}
|
|
529
|
+
then(onfulfilled, onrejected) {
|
|
530
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
525
531
|
}
|
|
526
532
|
async create(attributes = {}) {
|
|
527
533
|
return this.related.create(attributes, {
|
|
528
534
|
[this.relation.foreignKey]: this.parent.get(this.relation.localKey)
|
|
529
535
|
});
|
|
530
536
|
}
|
|
537
|
+
async save(related) {
|
|
538
|
+
const forced = {
|
|
539
|
+
[this.relation.foreignKey]: this.parent.get(this.relation.localKey)
|
|
540
|
+
};
|
|
541
|
+
const savable = related;
|
|
542
|
+
if (typeof savable.save === "function") {
|
|
543
|
+
savable.mergeAttributes?.(forced);
|
|
544
|
+
await savable.save();
|
|
545
|
+
return related;
|
|
546
|
+
}
|
|
547
|
+
return this.create(related);
|
|
548
|
+
}
|
|
531
549
|
async createMany(records) {
|
|
532
550
|
const created = [];
|
|
533
551
|
for (const attributes of records) {
|
|
@@ -575,11 +593,17 @@ class HasOneRelationQuery {
|
|
|
575
593
|
return this.get();
|
|
576
594
|
}
|
|
577
595
|
async count() {
|
|
578
|
-
return
|
|
596
|
+
return this.inner.count();
|
|
597
|
+
}
|
|
598
|
+
then(onfulfilled, onrejected) {
|
|
599
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
579
600
|
}
|
|
580
601
|
async create(attributes = {}) {
|
|
581
602
|
return this.inner.create(attributes);
|
|
582
603
|
}
|
|
604
|
+
async save(related) {
|
|
605
|
+
return this.inner.save(related);
|
|
606
|
+
}
|
|
583
607
|
}
|
|
584
608
|
|
|
585
609
|
class BelongsToRelationQuery {
|
|
@@ -587,12 +611,17 @@ class BelongsToRelationQuery {
|
|
|
587
611
|
related;
|
|
588
612
|
relation;
|
|
589
613
|
kind = "belongsTo";
|
|
614
|
+
extraWhere = {};
|
|
590
615
|
extraOptions = {};
|
|
591
616
|
constructor(parent, related, relation) {
|
|
592
617
|
this.parent = parent;
|
|
593
618
|
this.related = related;
|
|
594
619
|
this.relation = relation;
|
|
595
620
|
}
|
|
621
|
+
where(where) {
|
|
622
|
+
this.extraWhere = { ...this.extraWhere, ...where };
|
|
623
|
+
return this;
|
|
624
|
+
}
|
|
596
625
|
orderBy(orderBy) {
|
|
597
626
|
this.extraOptions = { ...this.extraOptions, orderBy };
|
|
598
627
|
return this;
|
|
@@ -606,10 +635,10 @@ class BelongsToRelationQuery {
|
|
|
606
635
|
}
|
|
607
636
|
toExistsClause(parentTable) {
|
|
608
637
|
const relatedTable = this.related.repository().getTable().name;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
};
|
|
638
|
+
const extra = buildAdvancedWhereClause(relatedTable, this.extraWhere, [], []);
|
|
639
|
+
const extraSql = extra.clause.replace(/^ WHERE /, "");
|
|
640
|
+
const sql = `SELECT 1 FROM ${quoteIdentifier(relatedTable)} WHERE ${qualifyColumn(relatedTable, this.relation.ownerKey)} = ${qualifyColumn(parentTable, this.relation.foreignKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
|
|
641
|
+
return { sql, params: extra.params };
|
|
613
642
|
}
|
|
614
643
|
async get() {
|
|
615
644
|
const foreign = this.parent.get(this.relation.foreignKey);
|
|
@@ -617,7 +646,7 @@ class BelongsToRelationQuery {
|
|
|
617
646
|
return null;
|
|
618
647
|
}
|
|
619
648
|
const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
|
|
620
|
-
let query = repository.query(asWhere({ [this.relation.ownerKey]: foreign }));
|
|
649
|
+
let query = repository.query(asWhere({ [this.relation.ownerKey]: foreign, ...this.extraWhere }));
|
|
621
650
|
if (this.extraOptions.orderBy) {
|
|
622
651
|
query = query.orderBy(this.extraOptions.orderBy);
|
|
623
652
|
}
|
|
@@ -627,6 +656,9 @@ class BelongsToRelationQuery {
|
|
|
627
656
|
async first() {
|
|
628
657
|
return this.get();
|
|
629
658
|
}
|
|
659
|
+
then(onfulfilled, onrejected) {
|
|
660
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
661
|
+
}
|
|
630
662
|
async associate(owner) {
|
|
631
663
|
await this.parent.getRepository().updateById(this.parent.id, {
|
|
632
664
|
[this.relation.foreignKey]: ownerId(owner, this.relation.ownerKey)
|
|
@@ -646,6 +678,7 @@ class BelongsToManyRelationQuery {
|
|
|
646
678
|
kind = "belongsToMany";
|
|
647
679
|
extraWhere = {};
|
|
648
680
|
extraOptions = {};
|
|
681
|
+
pivotValues = {};
|
|
649
682
|
constructor(parent, related, relation) {
|
|
650
683
|
this.parent = parent;
|
|
651
684
|
this.related = related;
|
|
@@ -659,7 +692,13 @@ class BelongsToManyRelationQuery {
|
|
|
659
692
|
this.extraOptions = { ...this.extraOptions, orderBy };
|
|
660
693
|
return this;
|
|
661
694
|
}
|
|
662
|
-
applyEagerLoad(
|
|
695
|
+
applyEagerLoad(query, alias) {
|
|
696
|
+
query.withBelongsToMany(alias, this.relation, this.related.repository(), this.extraOptions);
|
|
697
|
+
}
|
|
698
|
+
withPivotValues(values) {
|
|
699
|
+
this.pivotValues = { ...this.pivotValues, ...values };
|
|
700
|
+
return this;
|
|
701
|
+
}
|
|
663
702
|
hydrateEager(row, alias) {
|
|
664
703
|
const value = row[alias] ?? [];
|
|
665
704
|
const rows = Array.isArray(value) ? value : [];
|
|
@@ -699,13 +738,34 @@ class BelongsToManyRelationQuery {
|
|
|
699
738
|
return rows[0] ?? null;
|
|
700
739
|
}
|
|
701
740
|
async count() {
|
|
702
|
-
|
|
741
|
+
const parentId = this.parent.get(this.relation.parentKey);
|
|
742
|
+
const rows = await this.connection().unsafe(`SELECT COUNT(*) AS count FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
|
|
743
|
+
return Number(rows[0]?.count ?? 0);
|
|
744
|
+
}
|
|
745
|
+
then(onfulfilled, onrejected) {
|
|
746
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
703
747
|
}
|
|
704
748
|
async attach(ids) {
|
|
705
749
|
const list = Array.isArray(ids) ? ids : [ids];
|
|
706
750
|
const parentId = this.parent.get(this.relation.parentKey);
|
|
751
|
+
const extraKeys = Object.keys(this.pivotValues);
|
|
752
|
+
const extraColumns = extraKeys.length > 0 ? `, ${extraKeys.join(", ")}` : "";
|
|
753
|
+
const extraPlaceholders = extraKeys.map((_, index) => `$${index + 3}`).join(", ");
|
|
754
|
+
const extraValues = extraKeys.map((key) => this.pivotValues[key]);
|
|
707
755
|
for (const id of list) {
|
|
708
|
-
await this.connection().unsafe(`INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}) VALUES ($1, $2)`, [parentId, id]);
|
|
756
|
+
await this.connection().unsafe(extraKeys.length > 0 ? `INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}${extraColumns}) VALUES ($1, $2, ${extraPlaceholders})` : `INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}) VALUES ($1, $2)`, [parentId, id, ...extraValues]);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
async toggle(ids) {
|
|
760
|
+
const list = Array.isArray(ids) ? ids : [ids];
|
|
761
|
+
const parentId = this.parent.get(this.relation.parentKey);
|
|
762
|
+
for (const id of list) {
|
|
763
|
+
const existing = await this.connection().unsafe(`SELECT 1 FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1 AND ${String(this.relation.relatedPivotKey)} = $2 LIMIT 1`, [parentId, id]);
|
|
764
|
+
if (existing.length > 0) {
|
|
765
|
+
await this.detach(id);
|
|
766
|
+
} else {
|
|
767
|
+
await this.attach(id);
|
|
768
|
+
}
|
|
709
769
|
}
|
|
710
770
|
}
|
|
711
771
|
async detach(ids) {
|
|
@@ -777,6 +837,17 @@ class MorphManyRelationQuery {
|
|
|
777
837
|
const rows = await this.get();
|
|
778
838
|
return rows[0] ?? null;
|
|
779
839
|
}
|
|
840
|
+
async count() {
|
|
841
|
+
const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
|
|
842
|
+
return repository.query(asWhere({
|
|
843
|
+
[this.relation.morphTypeKey]: this.relation.morphType,
|
|
844
|
+
[this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
|
|
845
|
+
...this.extraWhere
|
|
846
|
+
})).count();
|
|
847
|
+
}
|
|
848
|
+
then(onfulfilled, onrejected) {
|
|
849
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
850
|
+
}
|
|
780
851
|
async create(attributes = {}) {
|
|
781
852
|
return this.related.create(attributes, {
|
|
782
853
|
[this.relation.morphTypeKey]: this.relation.morphType,
|
|
@@ -817,6 +888,15 @@ class MorphOneRelationQuery {
|
|
|
817
888
|
async get() {
|
|
818
889
|
return this.inner.first();
|
|
819
890
|
}
|
|
891
|
+
async first() {
|
|
892
|
+
return this.get();
|
|
893
|
+
}
|
|
894
|
+
async count() {
|
|
895
|
+
return this.inner.count();
|
|
896
|
+
}
|
|
897
|
+
then(onfulfilled, onrejected) {
|
|
898
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
899
|
+
}
|
|
820
900
|
async create(attributes = {}) {
|
|
821
901
|
return this.inner.create(attributes);
|
|
822
902
|
}
|
|
@@ -827,11 +907,16 @@ class MorphToRelationQuery {
|
|
|
827
907
|
relatedByType;
|
|
828
908
|
relation;
|
|
829
909
|
kind = "morphTo";
|
|
910
|
+
extraWhere = {};
|
|
830
911
|
constructor(parent, relatedByType, relation) {
|
|
831
912
|
this.parent = parent;
|
|
832
913
|
this.relatedByType = relatedByType;
|
|
833
914
|
this.relation = relation;
|
|
834
915
|
}
|
|
916
|
+
where(where) {
|
|
917
|
+
this.extraWhere = { ...this.extraWhere, ...where };
|
|
918
|
+
return this;
|
|
919
|
+
}
|
|
835
920
|
applyEagerLoad(query, alias) {
|
|
836
921
|
const repositories = new Map(Object.entries(this.relatedByType).map(([type, model]) => [
|
|
837
922
|
type,
|
|
@@ -849,9 +934,11 @@ class MorphToRelationQuery {
|
|
|
849
934
|
return { sql: "SELECT 1 WHERE 1 = 0", params: [] };
|
|
850
935
|
}
|
|
851
936
|
const relatedTable = related.repository().getTable();
|
|
937
|
+
const extra = buildAdvancedWhereClause(relatedTable.name, this.extraWhere, [], []);
|
|
938
|
+
const extraSql = extra.clause.replace(/^ WHERE /, "");
|
|
852
939
|
return {
|
|
853
|
-
sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable.name)} WHERE ${qualifyColumn(relatedTable.name, relatedTable.primaryKey)} = ${qualifyColumn(parentTable, this.relation.morphIdKey)}`,
|
|
854
|
-
params:
|
|
940
|
+
sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable.name)} WHERE ${qualifyColumn(relatedTable.name, relatedTable.primaryKey)} = ${qualifyColumn(parentTable, this.relation.morphIdKey)}${extraSql ? ` AND ${extraSql}` : ""}`,
|
|
941
|
+
params: extra.params
|
|
855
942
|
};
|
|
856
943
|
}
|
|
857
944
|
async get() {
|
|
@@ -862,9 +949,12 @@ class MorphToRelationQuery {
|
|
|
862
949
|
return null;
|
|
863
950
|
}
|
|
864
951
|
const table = related.repository().getTable();
|
|
865
|
-
const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id })).first();
|
|
952
|
+
const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere })).first();
|
|
866
953
|
return row ? related.newFromRecord(row) : null;
|
|
867
954
|
}
|
|
955
|
+
then(onfulfilled, onrejected) {
|
|
956
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
957
|
+
}
|
|
868
958
|
}
|
|
869
959
|
|
|
870
960
|
// ../../src/core/database/relationships.ts
|
|
@@ -1015,6 +1105,7 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
1015
1105
|
|
|
1016
1106
|
// ../../src/core/database/model.ts
|
|
1017
1107
|
var modelRepositories = new WeakMap;
|
|
1108
|
+
var namedModels = new Map;
|
|
1018
1109
|
var modelGlobalScopes = new WeakMap;
|
|
1019
1110
|
var modelObservers = new WeakMap;
|
|
1020
1111
|
var modelBooted = new WeakSet;
|
|
@@ -1032,38 +1123,59 @@ function accessorName(key) {
|
|
|
1032
1123
|
const pascal = key.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
1033
1124
|
return `get${pascal.charAt(0).toUpperCase()}${pascal.slice(1)}Attribute`;
|
|
1034
1125
|
}
|
|
1035
|
-
function
|
|
1036
|
-
|
|
1037
|
-
ensureBooted(model);
|
|
1038
|
-
const repository = resolveModelRepository(model);
|
|
1039
|
-
const dummy = statics.newFromRecord({});
|
|
1040
|
-
const method = dummy[name];
|
|
1041
|
-
if (typeof method !== "function") {
|
|
1042
|
-
throw new Error(`${model.name} has no relation method ${name}().`);
|
|
1043
|
-
}
|
|
1044
|
-
const relationQuery = method.call(dummy);
|
|
1045
|
-
constrain?.(relationQuery);
|
|
1046
|
-
const exists = relationQuery.toExistsClause(repository.getTable().name);
|
|
1047
|
-
const query = Model.query.call(model);
|
|
1048
|
-
return not ? query.whereNotExists(exists.sql, exists.params) : query.whereExists(exists.sql, exists.params);
|
|
1126
|
+
function isLoadableModel(value) {
|
|
1127
|
+
return Boolean(value && typeof value === "object" && typeof value.load === "function" && typeof value.loaded === "function" && typeof value.setLoaded === "function");
|
|
1049
1128
|
}
|
|
1050
|
-
async function
|
|
1051
|
-
|
|
1052
|
-
if (!head) {
|
|
1129
|
+
async function eagerLoadOnModels(models, paths) {
|
|
1130
|
+
if (models.length === 0 || paths.length === 0) {
|
|
1053
1131
|
return;
|
|
1054
1132
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1133
|
+
const grouped = new Map;
|
|
1134
|
+
for (const path of paths) {
|
|
1135
|
+
const [head, ...rest] = path.split(".");
|
|
1136
|
+
if (!head) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
const nested = rest.join(".");
|
|
1140
|
+
const existing = grouped.get(head) ?? [];
|
|
1141
|
+
if (nested) {
|
|
1142
|
+
existing.push(nested);
|
|
1143
|
+
}
|
|
1144
|
+
grouped.set(head, existing);
|
|
1058
1145
|
}
|
|
1059
|
-
const
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1146
|
+
for (const [head, nested] of grouped) {
|
|
1147
|
+
const unloaded = models.filter((model) => model.loaded(head) === undefined);
|
|
1148
|
+
if (unloaded.length > 0) {
|
|
1149
|
+
const first = unloaded[0];
|
|
1150
|
+
if (!first) {
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
const method = first[head];
|
|
1154
|
+
if (typeof method !== "function") {
|
|
1155
|
+
throw new Error(`${first.constructor.name} has no relation method ${head}().`);
|
|
1156
|
+
}
|
|
1157
|
+
const relationQuery = method.call(first);
|
|
1158
|
+
const query = first.getRepository().query();
|
|
1159
|
+
relationQuery.applyEagerLoad(query, head);
|
|
1160
|
+
const attached = await query.attachToRows(unloaded.map((model) => model.toObject()));
|
|
1161
|
+
for (const [index, model] of unloaded.entries()) {
|
|
1162
|
+
const row = attached[index] ?? model.toObject();
|
|
1163
|
+
model.setLoaded(head, relationQuery.hydrateEager(row, head));
|
|
1164
|
+
}
|
|
1064
1165
|
}
|
|
1166
|
+
if (nested.length === 0) {
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
const children = models.flatMap((model) => {
|
|
1170
|
+
const loaded = model.loaded(head);
|
|
1171
|
+
return Array.isArray(loaded) ? loaded : loaded ? [loaded] : [];
|
|
1172
|
+
});
|
|
1173
|
+
await eagerLoadOnModels(children.filter(isLoadableModel), nested);
|
|
1065
1174
|
}
|
|
1066
1175
|
}
|
|
1176
|
+
async function loadNested(model, path) {
|
|
1177
|
+
await eagerLoadOnModels([model], [path]);
|
|
1178
|
+
}
|
|
1067
1179
|
function resolveModelRepository(model) {
|
|
1068
1180
|
const repository = modelRepositories.get(model);
|
|
1069
1181
|
if (!repository) {
|
|
@@ -1071,6 +1183,48 @@ function resolveModelRepository(model) {
|
|
|
1071
1183
|
}
|
|
1072
1184
|
return repository;
|
|
1073
1185
|
}
|
|
1186
|
+
function registerModelClass(name, model) {
|
|
1187
|
+
namedModels.set(name, model);
|
|
1188
|
+
}
|
|
1189
|
+
function resolveRelated(related) {
|
|
1190
|
+
if (typeof related === "string") {
|
|
1191
|
+
const found = namedModels.get(related);
|
|
1192
|
+
if (!found) {
|
|
1193
|
+
throw new Error(`Model [${related}] is not registered. Call registerModelClass() first.`);
|
|
1194
|
+
}
|
|
1195
|
+
return found;
|
|
1196
|
+
}
|
|
1197
|
+
if (typeof related === "function" && typeof related.repository !== "function") {
|
|
1198
|
+
return related();
|
|
1199
|
+
}
|
|
1200
|
+
return related;
|
|
1201
|
+
}
|
|
1202
|
+
function inferRelationMethodName(callee) {
|
|
1203
|
+
const stack = new Error().stack ?? "";
|
|
1204
|
+
let seenCallee = false;
|
|
1205
|
+
for (const line of stack.split(`
|
|
1206
|
+
`)) {
|
|
1207
|
+
const match = /at (?:async )?(?:[^.\s]+\.)?(\w+)/.exec(line);
|
|
1208
|
+
const name = match?.[1];
|
|
1209
|
+
if (!name || name === "Error" || name === "inferRelationMethodName") {
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (!seenCallee) {
|
|
1213
|
+
if (name === callee) {
|
|
1214
|
+
seenCallee = true;
|
|
1215
|
+
}
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
if (name !== callee) {
|
|
1219
|
+
return name;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
function morphClassOf(model) {
|
|
1225
|
+
const statics = modelStatics(model.constructor === Function ? model : model.constructor);
|
|
1226
|
+
return statics.$morphClass ?? (model.constructor === Function ? model.name : model.constructor.name);
|
|
1227
|
+
}
|
|
1074
1228
|
function modelStatics(model) {
|
|
1075
1229
|
return model;
|
|
1076
1230
|
}
|
|
@@ -1100,6 +1254,11 @@ function hydrateValue(value, cast) {
|
|
|
1100
1254
|
case "bool":
|
|
1101
1255
|
case "boolean":
|
|
1102
1256
|
return value === true || value === 1 || value === "1" || value === "true";
|
|
1257
|
+
case "integer":
|
|
1258
|
+
case "int":
|
|
1259
|
+
return value === "" ? null : Number(value);
|
|
1260
|
+
case "hashed":
|
|
1261
|
+
return value;
|
|
1103
1262
|
default:
|
|
1104
1263
|
return value;
|
|
1105
1264
|
}
|
|
@@ -1117,6 +1276,11 @@ function dehydrateValue(value, cast) {
|
|
|
1117
1276
|
case "bool":
|
|
1118
1277
|
case "boolean":
|
|
1119
1278
|
return Boolean(value);
|
|
1279
|
+
case "integer":
|
|
1280
|
+
case "int":
|
|
1281
|
+
return value === "" ? null : Number(value);
|
|
1282
|
+
case "hashed":
|
|
1283
|
+
return value;
|
|
1120
1284
|
default:
|
|
1121
1285
|
return value;
|
|
1122
1286
|
}
|
|
@@ -1171,6 +1335,153 @@ function applyTimestampsOnUpdate(columns, values, enabled) {
|
|
|
1171
1335
|
return result;
|
|
1172
1336
|
}
|
|
1173
1337
|
|
|
1338
|
+
class ModelQuery {
|
|
1339
|
+
modelClass;
|
|
1340
|
+
query;
|
|
1341
|
+
eager = [];
|
|
1342
|
+
constructor(modelClass, query) {
|
|
1343
|
+
this.modelClass = modelClass;
|
|
1344
|
+
this.query = query;
|
|
1345
|
+
}
|
|
1346
|
+
with(...relations) {
|
|
1347
|
+
const statics = modelStatics(this.modelClass);
|
|
1348
|
+
ensureBooted(this.modelClass);
|
|
1349
|
+
const dummy = statics.newFromRecord({}, false);
|
|
1350
|
+
for (const path of relations) {
|
|
1351
|
+
const name = path.split(".")[0] ?? path;
|
|
1352
|
+
const method = dummy[name];
|
|
1353
|
+
if (typeof method !== "function") {
|
|
1354
|
+
throw new Error(`${this.modelClass.name} has no relation method ${name}().`);
|
|
1355
|
+
}
|
|
1356
|
+
const relationQuery = method.call(dummy);
|
|
1357
|
+
this.eager.push({ name, path, relationQuery });
|
|
1358
|
+
relationQuery.applyEagerLoad(this.query, name);
|
|
1359
|
+
}
|
|
1360
|
+
return this;
|
|
1361
|
+
}
|
|
1362
|
+
where(input) {
|
|
1363
|
+
this.query.where(input);
|
|
1364
|
+
return this;
|
|
1365
|
+
}
|
|
1366
|
+
orWhere(input) {
|
|
1367
|
+
this.query.orWhere(input);
|
|
1368
|
+
return this;
|
|
1369
|
+
}
|
|
1370
|
+
orderBy(orderBy) {
|
|
1371
|
+
this.query.orderBy(orderBy);
|
|
1372
|
+
return this;
|
|
1373
|
+
}
|
|
1374
|
+
limit(limit) {
|
|
1375
|
+
this.query.limit(limit);
|
|
1376
|
+
return this;
|
|
1377
|
+
}
|
|
1378
|
+
offset(offset) {
|
|
1379
|
+
this.query.offset(offset);
|
|
1380
|
+
return this;
|
|
1381
|
+
}
|
|
1382
|
+
whereNull(column) {
|
|
1383
|
+
this.query.whereNull(column);
|
|
1384
|
+
return this;
|
|
1385
|
+
}
|
|
1386
|
+
whereIn(column, values) {
|
|
1387
|
+
this.query.whereIn(column, values);
|
|
1388
|
+
return this;
|
|
1389
|
+
}
|
|
1390
|
+
whereExists(sql, params = []) {
|
|
1391
|
+
this.query.whereExists(sql, params);
|
|
1392
|
+
return this;
|
|
1393
|
+
}
|
|
1394
|
+
whereNotExists(sql, params = []) {
|
|
1395
|
+
this.query.whereNotExists(sql, params);
|
|
1396
|
+
return this;
|
|
1397
|
+
}
|
|
1398
|
+
whereHas(name, constrain) {
|
|
1399
|
+
return this.constrainExists(name, constrain, false);
|
|
1400
|
+
}
|
|
1401
|
+
has(name) {
|
|
1402
|
+
return this.constrainExists(name, undefined, false);
|
|
1403
|
+
}
|
|
1404
|
+
doesntHave(name) {
|
|
1405
|
+
return this.constrainExists(name, undefined, true);
|
|
1406
|
+
}
|
|
1407
|
+
whereDoesntHave(name, constrain) {
|
|
1408
|
+
return this.constrainExists(name, constrain, true);
|
|
1409
|
+
}
|
|
1410
|
+
withHasMany(...args) {
|
|
1411
|
+
this.query.withHasMany(...args);
|
|
1412
|
+
return this;
|
|
1413
|
+
}
|
|
1414
|
+
withBelongsTo(...args) {
|
|
1415
|
+
this.query.withBelongsTo(...args);
|
|
1416
|
+
return this;
|
|
1417
|
+
}
|
|
1418
|
+
withBelongsToMany(...args) {
|
|
1419
|
+
this.query.withBelongsToMany(...args);
|
|
1420
|
+
return this;
|
|
1421
|
+
}
|
|
1422
|
+
withMorphMany(...args) {
|
|
1423
|
+
this.query.withMorphMany(...args);
|
|
1424
|
+
return this;
|
|
1425
|
+
}
|
|
1426
|
+
withMorphOne(...args) {
|
|
1427
|
+
this.query.withMorphOne(...args);
|
|
1428
|
+
return this;
|
|
1429
|
+
}
|
|
1430
|
+
withMorphTo(...args) {
|
|
1431
|
+
this.query.withMorphTo(...args);
|
|
1432
|
+
return this;
|
|
1433
|
+
}
|
|
1434
|
+
async get() {
|
|
1435
|
+
const statics = modelStatics(this.modelClass);
|
|
1436
|
+
const rows = await this.query.get();
|
|
1437
|
+
const models = [];
|
|
1438
|
+
for (const row of rows) {
|
|
1439
|
+
const model = statics.newFromRecord(row, true);
|
|
1440
|
+
await runObservers(model, "retrieved");
|
|
1441
|
+
for (const { name, relationQuery } of this.eager) {
|
|
1442
|
+
model.setLoaded(name, relationQuery.hydrateEager(row, name));
|
|
1443
|
+
}
|
|
1444
|
+
models.push(model);
|
|
1445
|
+
}
|
|
1446
|
+
const nested = this.eager.filter((item) => item.path.includes(".")).map((item) => item.path);
|
|
1447
|
+
await eagerLoadOnModels(models.filter(isLoadableModel), nested);
|
|
1448
|
+
return models;
|
|
1449
|
+
}
|
|
1450
|
+
async first() {
|
|
1451
|
+
this.query.limit(1);
|
|
1452
|
+
const models = await this.get();
|
|
1453
|
+
return models[0] ?? null;
|
|
1454
|
+
}
|
|
1455
|
+
async find(id) {
|
|
1456
|
+
const primaryKey = resolveModelRepository(this.modelClass).getTable().primaryKey;
|
|
1457
|
+
return this.where({ [primaryKey]: id }).first();
|
|
1458
|
+
}
|
|
1459
|
+
async findOrFail(id, errorFactory) {
|
|
1460
|
+
const model = await this.find(id);
|
|
1461
|
+
if (model) {
|
|
1462
|
+
return model;
|
|
1463
|
+
}
|
|
1464
|
+
throw errorFactory?.(id) ?? new NotFoundError(`${this.modelClass.name} ${String(id)} not found.`);
|
|
1465
|
+
}
|
|
1466
|
+
then(onfulfilled, onrejected) {
|
|
1467
|
+
return this.get().then(onfulfilled ?? undefined, onrejected ?? undefined);
|
|
1468
|
+
}
|
|
1469
|
+
constrainExists(name, constrain, not) {
|
|
1470
|
+
const statics = modelStatics(this.modelClass);
|
|
1471
|
+
ensureBooted(this.modelClass);
|
|
1472
|
+
const repository = resolveModelRepository(this.modelClass);
|
|
1473
|
+
const dummy = statics.newFromRecord({});
|
|
1474
|
+
const method = dummy[name];
|
|
1475
|
+
if (typeof method !== "function") {
|
|
1476
|
+
throw new Error(`${this.modelClass.name} has no relation method ${name}().`);
|
|
1477
|
+
}
|
|
1478
|
+
const relationQuery = method.call(dummy);
|
|
1479
|
+
constrain?.(relationQuery);
|
|
1480
|
+
const exists = relationQuery.toExistsClause(repository.getTable().name);
|
|
1481
|
+
return not ? this.whereNotExists(exists.sql, exists.params) : this.whereExists(exists.sql, exists.params);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1174
1485
|
class Model {
|
|
1175
1486
|
attributes;
|
|
1176
1487
|
repository;
|
|
@@ -1181,6 +1492,7 @@ class Model {
|
|
|
1181
1492
|
static $hidden;
|
|
1182
1493
|
static $visible;
|
|
1183
1494
|
static $appends;
|
|
1495
|
+
static $morphClass;
|
|
1184
1496
|
_exists;
|
|
1185
1497
|
loadedRelations = {};
|
|
1186
1498
|
hiddenOverrides = [];
|
|
@@ -1190,6 +1502,7 @@ class Model {
|
|
|
1190
1502
|
this.attributes = attributes;
|
|
1191
1503
|
this.repository = repository;
|
|
1192
1504
|
this._exists = exists;
|
|
1505
|
+
this.attributes = modelStatics(this.constructor).hydrateAttributes(attributes);
|
|
1193
1506
|
}
|
|
1194
1507
|
getRepository() {
|
|
1195
1508
|
return this.repository;
|
|
@@ -1250,7 +1563,7 @@ class Model {
|
|
|
1250
1563
|
return this;
|
|
1251
1564
|
}
|
|
1252
1565
|
primaryKey() {
|
|
1253
|
-
|
|
1566
|
+
return this.repository.getTable().primaryKey;
|
|
1254
1567
|
}
|
|
1255
1568
|
static primaryKeyField() {
|
|
1256
1569
|
return resolveModelRepository(this).getTable().primaryKey;
|
|
@@ -1291,7 +1604,7 @@ class Model {
|
|
|
1291
1604
|
for (const scope of getGlobalScopes(this)) {
|
|
1292
1605
|
query = scope(query);
|
|
1293
1606
|
}
|
|
1294
|
-
return query;
|
|
1607
|
+
return new ModelQuery(this, query);
|
|
1295
1608
|
}
|
|
1296
1609
|
static newFromRecord(record, exists = true) {
|
|
1297
1610
|
const repository = resolveModelRepository(this);
|
|
@@ -1310,79 +1623,36 @@ class Model {
|
|
|
1310
1623
|
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1311
1624
|
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
1312
1625
|
const pending = statics.newFromRecord({ ...payload }, false);
|
|
1626
|
+
if (await runObservers(pending, "saving") === false) {
|
|
1627
|
+
throw new Error(`${this.name}.create() was cancelled by an observer.`);
|
|
1628
|
+
}
|
|
1313
1629
|
if (await runObservers(pending, "creating") === false) {
|
|
1314
1630
|
throw new Error(`${this.name}.create() was cancelled by an observer.`);
|
|
1315
1631
|
}
|
|
1316
1632
|
const record = await repository.create(payload);
|
|
1317
1633
|
const created = statics.fromRecord(record, repository, true);
|
|
1318
1634
|
await runObservers(created, "created");
|
|
1635
|
+
await runObservers(created, "saved");
|
|
1319
1636
|
return created;
|
|
1320
1637
|
}
|
|
1321
1638
|
static with(...relations) {
|
|
1322
|
-
|
|
1323
|
-
ensureBooted(this);
|
|
1324
|
-
const repository = resolveModelRepository(this);
|
|
1325
|
-
const dummy = statics.fromRecord({}, repository, false);
|
|
1326
|
-
const resolved = relations.map((path) => {
|
|
1327
|
-
const name = path.split(".")[0] ?? path;
|
|
1328
|
-
const method = dummy[name];
|
|
1329
|
-
if (typeof method !== "function") {
|
|
1330
|
-
throw new Error(`${this.name} has no relation method ${name}().`);
|
|
1331
|
-
}
|
|
1332
|
-
const relationQuery = method.call(dummy);
|
|
1333
|
-
return { name, path, relationQuery };
|
|
1334
|
-
});
|
|
1335
|
-
const query = Model.query.call(this);
|
|
1336
|
-
for (const { name, relationQuery } of resolved) {
|
|
1337
|
-
if (relationQuery.kind !== "belongsToMany") {
|
|
1338
|
-
relationQuery.applyEagerLoad(query, name);
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
return {
|
|
1342
|
-
async get() {
|
|
1343
|
-
const rows = await query.get();
|
|
1344
|
-
const models = [];
|
|
1345
|
-
for (const row of rows) {
|
|
1346
|
-
const model = statics.fromRecord(row, repository, true);
|
|
1347
|
-
for (const { name, path, relationQuery } of resolved) {
|
|
1348
|
-
if (relationQuery.kind === "belongsToMany") {
|
|
1349
|
-
await model.load(path.includes(".") ? path : name);
|
|
1350
|
-
continue;
|
|
1351
|
-
}
|
|
1352
|
-
model.setLoaded(name, relationQuery.hydrateEager(row, name));
|
|
1353
|
-
const nested = path.split(".").slice(1).join(".");
|
|
1354
|
-
if (nested) {
|
|
1355
|
-
await loadNested(model, path);
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
models.push(model);
|
|
1359
|
-
}
|
|
1360
|
-
return models;
|
|
1361
|
-
},
|
|
1362
|
-
async first() {
|
|
1363
|
-
const [model] = await this.get();
|
|
1364
|
-
return model ?? null;
|
|
1365
|
-
}
|
|
1366
|
-
};
|
|
1639
|
+
return Model.query.call(this).with(...relations);
|
|
1367
1640
|
}
|
|
1368
1641
|
static whereHas(name, constrain) {
|
|
1369
|
-
return
|
|
1642
|
+
return Model.query.call(this).whereHas(name, constrain);
|
|
1370
1643
|
}
|
|
1371
1644
|
static has(name) {
|
|
1372
|
-
return
|
|
1645
|
+
return Model.query.call(this).has(name);
|
|
1373
1646
|
}
|
|
1374
1647
|
static doesntHave(name) {
|
|
1375
|
-
return
|
|
1648
|
+
return Model.query.call(this).doesntHave(name);
|
|
1376
1649
|
}
|
|
1377
1650
|
static whereDoesntHave(name, constrain) {
|
|
1378
|
-
return
|
|
1651
|
+
return Model.query.call(this).whereDoesntHave(name, constrain);
|
|
1379
1652
|
}
|
|
1380
1653
|
static async find(id) {
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
const primaryKey = repository.getTable().primaryKey;
|
|
1384
|
-
const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
|
|
1385
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1654
|
+
const primaryKey = resolveModelRepository(this).getTable().primaryKey;
|
|
1655
|
+
return Model.query.call(this).where({ [primaryKey]: id }).first();
|
|
1386
1656
|
}
|
|
1387
1657
|
static async findOrFail(id, errorFactory) {
|
|
1388
1658
|
const model = await Model.find.call(this, id);
|
|
@@ -1392,8 +1662,6 @@ class Model {
|
|
|
1392
1662
|
throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
|
|
1393
1663
|
}
|
|
1394
1664
|
static async all(options = {}) {
|
|
1395
|
-
const statics = modelStatics(this);
|
|
1396
|
-
const repository = resolveModelRepository(this);
|
|
1397
1665
|
let query = Model.query.call(this);
|
|
1398
1666
|
if (options.orderBy) {
|
|
1399
1667
|
query = query.orderBy(options.orderBy);
|
|
@@ -1401,21 +1669,17 @@ class Model {
|
|
|
1401
1669
|
if (options.limit !== undefined) {
|
|
1402
1670
|
query = query.limit(options.limit);
|
|
1403
1671
|
}
|
|
1404
|
-
|
|
1405
|
-
return rows.map((row) => statics.fromRecord(row, repository, true));
|
|
1672
|
+
return query.get();
|
|
1406
1673
|
}
|
|
1407
1674
|
static where(where) {
|
|
1408
1675
|
return Model.query.call(this).where(where);
|
|
1409
1676
|
}
|
|
1410
1677
|
static async firstWhere(where, options = {}) {
|
|
1411
|
-
const statics = modelStatics(this);
|
|
1412
|
-
const repository = resolveModelRepository(this);
|
|
1413
1678
|
let query = Model.query.call(this).where(where);
|
|
1414
1679
|
if (options.orderBy) {
|
|
1415
1680
|
query = query.orderBy(options.orderBy);
|
|
1416
1681
|
}
|
|
1417
|
-
|
|
1418
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1682
|
+
return query.first();
|
|
1419
1683
|
}
|
|
1420
1684
|
static async firstOrNew(where, values = {}) {
|
|
1421
1685
|
const existing = await Model.firstWhere.call(this, where);
|
|
@@ -1444,6 +1708,9 @@ class Model {
|
|
|
1444
1708
|
const casts = ModelClass.$casts ?? {};
|
|
1445
1709
|
const table = this.repository.getTable();
|
|
1446
1710
|
const updating = this.$exists;
|
|
1711
|
+
if (await runObservers(this, "saving") === false) {
|
|
1712
|
+
return this;
|
|
1713
|
+
}
|
|
1447
1714
|
if (await runObservers(this, updating ? "updating" : "creating") === false) {
|
|
1448
1715
|
return this;
|
|
1449
1716
|
}
|
|
@@ -1452,6 +1719,7 @@ class Model {
|
|
|
1452
1719
|
const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
1453
1720
|
this.attributes = ModelClass.hydrateAttributes(record2);
|
|
1454
1721
|
await runObservers(this, "updated");
|
|
1722
|
+
await runObservers(this, "saved");
|
|
1455
1723
|
return this;
|
|
1456
1724
|
}
|
|
1457
1725
|
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
@@ -1461,6 +1729,7 @@ class Model {
|
|
|
1461
1729
|
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1462
1730
|
this._exists = true;
|
|
1463
1731
|
await runObservers(this, "created");
|
|
1732
|
+
await runObservers(this, "saved");
|
|
1464
1733
|
return this;
|
|
1465
1734
|
}
|
|
1466
1735
|
async update(changes) {
|
|
@@ -1528,23 +1797,26 @@ class Model {
|
|
|
1528
1797
|
}
|
|
1529
1798
|
hasMany(related, foreignKey, localKey) {
|
|
1530
1799
|
const table = this.repository.getTable();
|
|
1531
|
-
|
|
1532
|
-
|
|
1800
|
+
const relatedClass = resolveRelated(related);
|
|
1801
|
+
return new HasManyRelationQuery(this, relatedClass, hasMany({
|
|
1802
|
+
name: relatedClass.repository().getTable().name,
|
|
1533
1803
|
localKey: localKey ?? table.primaryKey,
|
|
1534
1804
|
foreignKey: foreignKey ?? foreignKeyFromTable(table.name)
|
|
1535
1805
|
}));
|
|
1536
1806
|
}
|
|
1537
1807
|
hasOne(related, foreignKey, localKey) {
|
|
1538
1808
|
const table = this.repository.getTable();
|
|
1539
|
-
|
|
1540
|
-
|
|
1809
|
+
const relatedClass = resolveRelated(related);
|
|
1810
|
+
return new HasOneRelationQuery(this, relatedClass, hasOne({
|
|
1811
|
+
name: relatedClass.repository().getTable().name,
|
|
1541
1812
|
localKey: localKey ?? table.primaryKey,
|
|
1542
1813
|
foreignKey: foreignKey ?? foreignKeyFromTable(table.name)
|
|
1543
1814
|
}));
|
|
1544
1815
|
}
|
|
1545
1816
|
belongsTo(related, foreignKey, ownerKey) {
|
|
1546
|
-
const
|
|
1547
|
-
|
|
1817
|
+
const relatedClass = resolveRelated(related);
|
|
1818
|
+
const relatedTable = relatedClass.repository().getTable();
|
|
1819
|
+
return new BelongsToRelationQuery(this, relatedClass, belongsTo({
|
|
1548
1820
|
name: relatedTable.name,
|
|
1549
1821
|
foreignKey: foreignKey ?? foreignKeyFromTable(relatedTable.name),
|
|
1550
1822
|
ownerKey: ownerKey ?? relatedTable.primaryKey
|
|
@@ -1552,8 +1824,9 @@ class Model {
|
|
|
1552
1824
|
}
|
|
1553
1825
|
belongsToMany(related, pivotTable, foreignPivotKey, relatedPivotKey) {
|
|
1554
1826
|
const table = this.repository.getTable();
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1827
|
+
const relatedClass = resolveRelated(related);
|
|
1828
|
+
const relatedTable = relatedClass.repository().getTable();
|
|
1829
|
+
return new BelongsToManyRelationQuery(this, relatedClass, belongsToMany({
|
|
1557
1830
|
name: relatedTable.name,
|
|
1558
1831
|
pivotTable: pivotTable ?? pivotTableName(table.name, relatedTable.name),
|
|
1559
1832
|
parentKey: table.primaryKey,
|
|
@@ -1562,31 +1835,38 @@ class Model {
|
|
|
1562
1835
|
relatedPivotKey: relatedPivotKey ?? foreignKeyFromTable(relatedTable.name)
|
|
1563
1836
|
}));
|
|
1564
1837
|
}
|
|
1565
|
-
morphMany(related, morphName, typeKey, idKey) {
|
|
1838
|
+
morphMany(related, morphName, typeKey, idKey, morphType) {
|
|
1566
1839
|
const table = this.repository.getTable();
|
|
1567
|
-
|
|
1840
|
+
const relatedClass = resolveRelated(related);
|
|
1841
|
+
return new MorphManyRelationQuery(this, relatedClass, morphMany({
|
|
1568
1842
|
name: morphName,
|
|
1569
1843
|
localKey: table.primaryKey,
|
|
1570
1844
|
morphTypeKey: typeKey ?? `${morphName}_type`,
|
|
1571
1845
|
morphIdKey: idKey ?? `${morphName}_id`,
|
|
1572
|
-
morphType:
|
|
1846
|
+
morphType: morphType ?? morphClassOf(this)
|
|
1573
1847
|
}));
|
|
1574
1848
|
}
|
|
1575
|
-
morphOne(related, morphName, typeKey, idKey) {
|
|
1849
|
+
morphOne(related, morphName, typeKey, idKey, morphType) {
|
|
1576
1850
|
const table = this.repository.getTable();
|
|
1577
|
-
|
|
1851
|
+
const relatedClass = resolveRelated(related);
|
|
1852
|
+
return new MorphOneRelationQuery(this, relatedClass, morphOne({
|
|
1578
1853
|
name: morphName,
|
|
1579
1854
|
localKey: table.primaryKey,
|
|
1580
1855
|
morphTypeKey: typeKey ?? `${morphName}_type`,
|
|
1581
1856
|
morphIdKey: idKey ?? `${morphName}_id`,
|
|
1582
|
-
morphType:
|
|
1857
|
+
morphType: morphType ?? morphClassOf(this)
|
|
1583
1858
|
}));
|
|
1584
1859
|
}
|
|
1585
|
-
morphTo(relatedByType, morphName
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1860
|
+
morphTo(relatedByType, morphName, typeKey, idKey) {
|
|
1861
|
+
const resolvedName = morphName ?? inferRelationMethodName("morphTo");
|
|
1862
|
+
if (!resolvedName) {
|
|
1863
|
+
throw new Error(`${this.constructor.name}.morphTo() needs an explicit morph name (Laravel infers it from the relation method).`);
|
|
1864
|
+
}
|
|
1865
|
+
const resolvedMap = Object.fromEntries(Object.entries(relatedByType).map(([type, related]) => [type, resolveRelated(related)]));
|
|
1866
|
+
return new MorphToRelationQuery(this, resolvedMap, morphTo({
|
|
1867
|
+
name: resolvedName,
|
|
1868
|
+
morphTypeKey: typeKey ?? `${resolvedName}_type`,
|
|
1869
|
+
morphIdKey: idKey ?? `${resolvedName}_id`
|
|
1590
1870
|
}));
|
|
1591
1871
|
}
|
|
1592
1872
|
async load(...names) {
|
|
@@ -1618,6 +1898,14 @@ class Model {
|
|
|
1618
1898
|
}
|
|
1619
1899
|
function registerModelRepository(model, repository) {
|
|
1620
1900
|
modelRepositories.set(model, repository);
|
|
1901
|
+
const name = model.name;
|
|
1902
|
+
if (name) {
|
|
1903
|
+
namedModels.set(name, model);
|
|
1904
|
+
}
|
|
1905
|
+
const morphClass = model.$morphClass;
|
|
1906
|
+
if (morphClass) {
|
|
1907
|
+
namedModels.set(morphClass, model);
|
|
1908
|
+
}
|
|
1621
1909
|
ensureBooted(model);
|
|
1622
1910
|
return model;
|
|
1623
1911
|
}
|
|
@@ -1627,6 +1915,7 @@ export {
|
|
|
1627
1915
|
HasManyRelationQuery,
|
|
1628
1916
|
HasOneRelationQuery,
|
|
1629
1917
|
Model,
|
|
1918
|
+
ModelQuery,
|
|
1630
1919
|
MorphManyRelationQuery,
|
|
1631
1920
|
MorphOneRelationQuery,
|
|
1632
1921
|
MorphToRelationQuery,
|
|
@@ -1634,5 +1923,6 @@ export {
|
|
|
1634
1923
|
dehydrateValue,
|
|
1635
1924
|
filterMassAssignable,
|
|
1636
1925
|
hydrateValue,
|
|
1926
|
+
registerModelClass,
|
|
1637
1927
|
registerModelRepository
|
|
1638
1928
|
};
|