@getstrata/core 0.5.99 → 0.5.101
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 +14 -0
- package/dist/core/database/baseRepository.d.ts +5 -1
- package/dist/core/database/index.d.ts +3 -3
- package/dist/core/database/model.d.ts +23 -2
- package/dist/core/database/relationQuery.d.ts +22 -3
- package/dist/core/database/relationships.d.ts +25 -3
- package/dist/core/database/repositoryQuery.d.ts +4 -1
- package/dist/entries/database/model.js +225 -24
- package/dist/entries/database/relationships.js +114 -21
- package/dist/entries/database/repositoryQuery.js +268 -6
- package/dist/entries/openapi/generator.js +22 -2
- package/dist/index.js +330 -37
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.101
|
|
4
|
+
|
|
5
|
+
- Tenant middleware falls back with explicit branches when a member, admin, or guest tenant lookup misses, so the request still scopes to the default tenant.
|
|
6
|
+
|
|
7
|
+
## 0.5.100
|
|
8
|
+
|
|
9
|
+
HiroApp dogfood of 0.5.99: eager `with()` / nested `load("a.b")` missed related rows when a Postgres int4 PK arrived as a JS `number` and an int8 FK as a `bigint`. Map matching used `===`.
|
|
10
|
+
|
|
11
|
+
- Relation indexers and eager attach compare keys with `relationMatchKey` (`1`, `1n`, `"1"` match).
|
|
12
|
+
- Sequential `load("position")` / `application.position()` already used SQL `get()` and were unaffected.
|
|
13
|
+
- `registerModelRepository(User, users)` already names `User` and `$morphClass`. Do not also call `registerModelClass("User", User)`. `registerModelClass` is only for an alias that is neither `constructor.name` nor `$morphClass`.
|
|
14
|
+
|
|
15
|
+
Still non-conforming (honest): Bun cannot infer `morphTo()` method names. `hashed` cast is a no-op. `Factory.has()` without a bound model still needs an explicit FK.
|
|
16
|
+
|
|
3
17
|
## 0.5.99
|
|
4
18
|
|
|
5
19
|
HiroApp dogfood of 0.5.98 found lookalike APIs. This release matches Laravel call shape and SQL, not just export names.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CursorPaginatedResult, type PaginatedResult } from "../pagination/index.ts";
|
|
2
|
-
import { type BelongsToManyRelation, type BelongsToRelation, type HasManyRelation, type MorphManyRelation, type MorphOneRelation, type MorphToRelation } from "./relationships.ts";
|
|
2
|
+
import { type BelongsToManyRelation, type BelongsToRelation, type HasManyRelation, type HasManyThroughRelation, type MorphManyRelation, type MorphOneRelation, type MorphToRelation } from "./relationships.ts";
|
|
3
3
|
import { RepositoryQuery } from "./repositoryQuery.ts";
|
|
4
4
|
import type { TableDefinition } from "./table.ts";
|
|
5
5
|
import type { MutationValues, QueryOptions, QueryWhere, UpdateValues } from "./types.ts";
|
|
@@ -59,6 +59,10 @@ declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TE
|
|
|
59
59
|
}>>;
|
|
60
60
|
findByHasManyRelation<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, parentId: TParent[LocalKey], options?: Omit<QueryOptions<TEntity>, "where">): Promise<TEntity[]>;
|
|
61
61
|
loadHasManyForParents<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(parents: readonly TParent[], relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity[]>>;
|
|
62
|
+
findHasManyThrough<TParent extends object, LocalKey extends keyof TParent & string, FirstKey extends string, SecondLocalKey extends string, SecondKey extends keyof TEntity & string>(parentId: unknown, relation: HasManyThroughRelation<TParent, TEntity, LocalKey, FirstKey, SecondLocalKey, SecondKey>, options?: QueryOptions<TEntity>): Promise<TEntity[]>;
|
|
63
|
+
loadHasManyThroughForParents<TParent extends object, LocalKey extends keyof TParent & string, FirstKey extends string, SecondLocalKey extends string, SecondKey extends keyof TEntity & string>(parents: readonly TParent[], relation: HasManyThroughRelation<TParent, TEntity, LocalKey, FirstKey, SecondLocalKey, SecondKey>, options?: QueryOptions<TEntity>): Promise<Map<TParent[LocalKey], TEntity[]>>;
|
|
64
|
+
private throughSoftDeleteClause;
|
|
65
|
+
private buildThroughWhere;
|
|
62
66
|
loadBelongsToForParents<TChild extends object, TParent extends object, ForeignKey extends keyof TChild & string, OwnerKey extends keyof TParent & string>(children: readonly TChild[], relation: BelongsToRelation<TChild, TParent, ForeignKey, OwnerKey>, parentRepository: BaseRepository<TParent, OwnerKey>, options?: Omit<QueryOptions<TParent>, "where">): Promise<Map<TChild[ForeignKey], TParent>>;
|
|
63
67
|
loadMorphManyForParents<TParent extends object, LocalKey extends keyof TParent & string, MorphTypeKey extends keyof TEntity & string, MorphIdKey extends keyof TEntity & string>(parents: readonly TParent[], relation: MorphManyRelation<TParent, TEntity, LocalKey, MorphTypeKey, MorphIdKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity[]>>;
|
|
64
68
|
loadMorphOneForParents<TParent extends object, LocalKey extends keyof TParent & string, MorphTypeKey extends keyof TEntity & string, MorphIdKey extends keyof TEntity & string>(parents: readonly TParent[], relation: MorphOneRelation<TParent, TEntity, LocalKey, MorphTypeKey, MorphIdKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity | undefined>>;
|
|
@@ -5,10 +5,10 @@ export { mapDatabaseError, withDatabaseErrorHandling } from "./errors.ts";
|
|
|
5
5
|
export { Factory } from "./factory.ts";
|
|
6
6
|
export { foreignKeyFromTable, pivotTableName, singularize } from "./inflection.ts";
|
|
7
7
|
export type { CastType, GlobalScopeFn, ModelConstructor } from "./model.ts";
|
|
8
|
-
export { applyCasts, BelongsToManyRelationQuery, BelongsToRelationQuery, dehydrateValue, filterMassAssignable, HasManyRelationQuery, HasOneRelationQuery, hydrateValue, Model, ModelQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, registerModelClass, registerModelRepository, } from "./model.ts";
|
|
8
|
+
export { applyCasts, BelongsToManyRelationQuery, BelongsToRelationQuery, dehydrateValue, filterMassAssignable, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, hydrateValue, Model, ModelQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, registerModelClass, registerModelRepository, } from "./model.ts";
|
|
9
9
|
export { buildAdvancedWhereClause, buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildJoinClause, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, parseQualifiedColumn, qualifyColumn, quoteIdentifier, resolveQualifiedColumn, resolveSoftDeleteColumn, } from "./query.ts";
|
|
10
|
-
export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "./relationships.ts";
|
|
11
|
-
export { belongsTo, belongsToMany, hasMany, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "./relationships.ts";
|
|
10
|
+
export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "./relationships.ts";
|
|
11
|
+
export { belongsTo, belongsToMany, hasMany, hasManyThrough, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasManyThroughRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "./relationships.ts";
|
|
12
12
|
export { RepositoryQuery } from "./repositoryQuery.ts";
|
|
13
13
|
export type { BlueprintAction, BlueprintCallback, ColumnKind, DatabaseDriver, ForeignKeyOptions, Grammar, IndexDefinition, IndexKind, ResolveDatabaseDriverOptions, SchemaBuilder, } from "./schema/index.ts";
|
|
14
14
|
export { Blueprint, ColumnDefinition, compileBlueprint, createSchemaBuilder, ForeignIdColumnDefinition, grammarForDriver, inferReferencedTable, MySqlGrammar, PostgresGrammar, resolveDatabaseDriver, Schema, SqliteGrammar, UnsupportedSchemaFeatureError, } from "./schema/index.ts";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type BaseRepository from "./baseRepository.ts";
|
|
2
2
|
import type { AnyRelationQuery, RelatedModelClass } from "./relationQuery.ts";
|
|
3
|
-
import { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery } from "./relationQuery.ts";
|
|
3
|
+
import { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery } from "./relationQuery.ts";
|
|
4
4
|
import type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasOneRelation } from "./relationships.ts";
|
|
5
5
|
import type { RepositoryQuery } from "./repositoryQuery.ts";
|
|
6
6
|
import type { QueryOptions, QueryWhere } from "./types.ts";
|
|
@@ -25,6 +25,7 @@ type ModelObserver = {
|
|
|
25
25
|
deleting?: (model: AnyModel) => unknown;
|
|
26
26
|
deleted?: (model: AnyModel) => unknown;
|
|
27
27
|
};
|
|
28
|
+
/** Alias for `hasMany("Name")` when `Name` is not `constructor.name` or `$morphClass`. */
|
|
28
29
|
declare function registerModelClass(name: string, model: object): void;
|
|
29
30
|
declare function hydrateValue(value: unknown, cast: CastType): unknown;
|
|
30
31
|
declare function dehydrateValue(value: unknown, cast: CastType): unknown;
|
|
@@ -55,6 +56,9 @@ declare class ModelQuery {
|
|
|
55
56
|
withMorphMany(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withMorphMany"]>): this;
|
|
56
57
|
withMorphOne(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withMorphOne"]>): this;
|
|
57
58
|
withMorphTo(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withMorphTo"]>): this;
|
|
59
|
+
withHasManyThrough(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withHasManyThrough"]>): this;
|
|
60
|
+
withTrashed(): this;
|
|
61
|
+
onlyTrashed(): this;
|
|
58
62
|
get(): Promise<Array<Model<Record<string, unknown>, "id">>>;
|
|
59
63
|
first(): Promise<Model<Record<string, unknown>, "id"> | null>;
|
|
60
64
|
find(id: unknown): Promise<Model<Record<string, unknown>, "id"> | null>;
|
|
@@ -102,6 +106,21 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
|
|
|
102
106
|
static newFromRecord(this: object, record: object, exists?: boolean): Model<Record<string, unknown>, "id">;
|
|
103
107
|
static create(this: object, attributes: Record<string, unknown>, forced?: Record<string, unknown>): Promise<Model<Record<string, unknown>, "id">>;
|
|
104
108
|
static with(this: object, ...relations: string[]): ModelQuery;
|
|
109
|
+
static withTrashed(this: object): ModelQuery;
|
|
110
|
+
static onlyTrashed(this: object): ModelQuery;
|
|
111
|
+
static chunk(this: object, count: number, callback: (models: Array<Model<Record<string, unknown>, "id">>) => Promise<boolean | void>): Promise<void>;
|
|
112
|
+
static cursorPaginate(this: object, options: {
|
|
113
|
+
perPage: number;
|
|
114
|
+
cursor?: unknown;
|
|
115
|
+
}): Promise<{
|
|
116
|
+
data: Array<Model<Record<string, unknown>, "id">>;
|
|
117
|
+
meta: {
|
|
118
|
+
per_page: number;
|
|
119
|
+
next_cursor: unknown;
|
|
120
|
+
prev_cursor: unknown;
|
|
121
|
+
has_more: boolean;
|
|
122
|
+
};
|
|
123
|
+
}>;
|
|
105
124
|
static whereHas(this: object, name: string, constrain?: (query: AnyRelationQuery) => void): ModelQuery;
|
|
106
125
|
static has(this: object, name: string): ModelQuery;
|
|
107
126
|
static doesntHave(this: object, name: string): ModelQuery;
|
|
@@ -126,6 +145,7 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
|
|
|
126
145
|
hasMany<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, foreignKey?: keyof TRelated & string, localKey?: PrimaryKey): HasManyRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
|
|
127
146
|
hasOne<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, foreignKey?: keyof TRelated & string, localKey?: PrimaryKey): HasOneRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
|
|
128
147
|
belongsTo<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, foreignKey?: keyof TEntity & string, ownerKey?: RelatedKey): BelongsToRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
|
|
148
|
+
hasManyThrough<TRelated extends object, TThrough extends object, RelatedKey extends keyof TRelated & string, ThroughKey extends keyof TThrough & string>(related: RelatedRef<TRelated, RelatedKey>, through: RelatedRef<TThrough, ThroughKey>, firstKey?: string, secondKey?: keyof TRelated & string, localKey?: PrimaryKey, secondLocalKey?: ThroughKey): HasManyThroughRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
|
|
129
149
|
belongsToMany<TRelated extends object, RelatedKey extends keyof TRelated & string, Pivot extends object = Record<string, unknown>>(related: RelatedRef<TRelated, RelatedKey>, pivotTable?: string, foreignPivotKey?: keyof Pivot & string, relatedPivotKey?: keyof Pivot & string): BelongsToManyRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey, Pivot>;
|
|
130
150
|
morphMany<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, morphName: string, typeKey?: keyof TRelated & string, idKey?: keyof TRelated & string, morphType?: string): MorphManyRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
|
|
131
151
|
morphOne<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, morphName: string, typeKey?: keyof TRelated & string, idKey?: keyof TRelated & string, morphType?: string): MorphOneRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
|
|
@@ -135,7 +155,8 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
|
|
|
135
155
|
setLoaded(name: string, value: unknown): this;
|
|
136
156
|
mergeAttributes(patch: Partial<TEntity>): this;
|
|
137
157
|
}
|
|
158
|
+
/** Binds a Model class to its table/connection and names it for `hasMany("User")`. */
|
|
138
159
|
declare function registerModelRepository<TModelClass>(model: TModelClass, repository: object): TModelClass;
|
|
139
|
-
export { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, } from "./relationQuery.ts";
|
|
160
|
+
export { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, } from "./relationQuery.ts";
|
|
140
161
|
export type { CastType, GlobalScopeFn, ModelClassType, ModelConstructor };
|
|
141
162
|
export { applyCasts, dehydrateValue, filterMassAssignable, hydrateValue, Model, ModelQuery, registerModelClass, registerModelRepository, };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type BaseRepository from "./baseRepository.ts";
|
|
2
|
-
import type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation } from "./relationships.ts";
|
|
2
|
+
import type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation } from "./relationships.ts";
|
|
3
3
|
import type { RepositoryQuery } from "./repositoryQuery.ts";
|
|
4
4
|
import type { QueryOptions, QueryWhere } from "./types.ts";
|
|
5
5
|
type RelatedRecord = {
|
|
@@ -17,7 +17,7 @@ interface RelationHost<TEntity extends object, PrimaryKey extends keyof TEntity
|
|
|
17
17
|
get<K extends keyof TEntity>(key: K): TEntity[K];
|
|
18
18
|
getRepository(): BaseRepository<TEntity, PrimaryKey>;
|
|
19
19
|
}
|
|
20
|
-
type RelationKind = "hasMany" | "hasOne" | "belongsTo" | "belongsToMany" | "morphMany" | "morphOne" | "morphTo";
|
|
20
|
+
type RelationKind = "hasMany" | "hasOne" | "belongsTo" | "belongsToMany" | "hasManyThrough" | "morphMany" | "morphOne" | "morphTo";
|
|
21
21
|
type ExistsClause = {
|
|
22
22
|
sql: string;
|
|
23
23
|
params: unknown[];
|
|
@@ -159,6 +159,25 @@ declare class MorphToRelationQuery<TChild extends object, ChildKey extends keyof
|
|
|
159
159
|
get(): Promise<RelatedRecord | null>;
|
|
160
160
|
then(onfulfilled?: ((value: RelatedRecord | null) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
|
|
161
161
|
}
|
|
162
|
+
declare class HasManyThroughRelationQuery<TParent extends object, ParentKey extends keyof TParent & string, TFar extends object, FarKey extends keyof TFar & string> {
|
|
163
|
+
private readonly parent;
|
|
164
|
+
private readonly related;
|
|
165
|
+
readonly relation: HasManyThroughRelation<TParent, TFar, ParentKey, string, string, keyof TFar & string>;
|
|
166
|
+
readonly kind: RelationKind;
|
|
167
|
+
private extraWhere;
|
|
168
|
+
private extraOptions;
|
|
169
|
+
constructor(parent: RelationHost<TParent, ParentKey>, related: RelatedModelClass<TFar, FarKey>, relation: HasManyThroughRelation<TParent, TFar, ParentKey, string, string, keyof TFar & string>);
|
|
170
|
+
where(where: QueryWhere<TFar>): this;
|
|
171
|
+
orderBy(orderBy: QueryOptions<TFar>["orderBy"]): this;
|
|
172
|
+
limit(limit: number): this;
|
|
173
|
+
applyEagerLoad(query: RepositoryQuery<TParent, ParentKey>, alias: string): void;
|
|
174
|
+
hydrateEager(row: Record<string, unknown>, alias: string): unknown;
|
|
175
|
+
toExistsClause(parentTable: string): ExistsClause;
|
|
176
|
+
get(): Promise<RelatedRecord[]>;
|
|
177
|
+
first(): Promise<RelatedRecord | null>;
|
|
178
|
+
count(): Promise<number>;
|
|
179
|
+
then(onfulfilled?: ((value: RelatedRecord[]) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
|
|
180
|
+
}
|
|
162
181
|
type AnyRelationQuery = {
|
|
163
182
|
kind: RelationKind;
|
|
164
183
|
applyEagerLoad(query: unknown, alias: string): void;
|
|
@@ -169,4 +188,4 @@ type AnyRelationQuery = {
|
|
|
169
188
|
then?: (onfulfilled?: ((value: unknown) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null) => Promise<unknown>;
|
|
170
189
|
};
|
|
171
190
|
export type { AnyRelationQuery, RelatedModelClass, RelatedRecord, RelationHost };
|
|
172
|
-
export { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, };
|
|
191
|
+
export { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, };
|
|
@@ -25,6 +25,16 @@ interface BelongsToManyRelation<TParent, TRelated, Pivot extends object, ParentK
|
|
|
25
25
|
foreignPivotKey: ForeignPivotKey;
|
|
26
26
|
relatedPivotKey: RelatedPivotKey;
|
|
27
27
|
}
|
|
28
|
+
interface HasManyThroughRelation<TParent, TFar, LocalKey extends keyof TParent & string = keyof TParent & string, FirstKey extends string = string, SecondLocalKey extends string = string, SecondKey extends keyof TFar & string = keyof TFar & string> {
|
|
29
|
+
type: "hasManyThrough";
|
|
30
|
+
name: string;
|
|
31
|
+
throughTable: string;
|
|
32
|
+
localKey: LocalKey;
|
|
33
|
+
firstKey: FirstKey;
|
|
34
|
+
secondLocalKey: SecondLocalKey;
|
|
35
|
+
secondKey: SecondKey;
|
|
36
|
+
throughParentKey?: string;
|
|
37
|
+
}
|
|
28
38
|
declare function hasMany<TParent, TChild, LocalKey extends keyof TParent & string = keyof TParent & string, ForeignKey extends keyof TChild & string = keyof TChild & string>(definition: {
|
|
29
39
|
name: string;
|
|
30
40
|
localKey: LocalKey;
|
|
@@ -40,6 +50,15 @@ declare function belongsTo<TChild, TParent, ForeignKey extends keyof TChild & st
|
|
|
40
50
|
foreignKey: ForeignKey;
|
|
41
51
|
ownerKey: OwnerKey;
|
|
42
52
|
}): BelongsToRelation<TChild, TParent, ForeignKey, OwnerKey>;
|
|
53
|
+
declare function hasManyThrough<TParent, TFar, LocalKey extends keyof TParent & string = keyof TParent & string, FirstKey extends string = string, SecondLocalKey extends string = string, SecondKey extends keyof TFar & string = keyof TFar & string>(definition: {
|
|
54
|
+
name: string;
|
|
55
|
+
throughTable: string;
|
|
56
|
+
localKey: LocalKey;
|
|
57
|
+
firstKey: FirstKey;
|
|
58
|
+
secondLocalKey: SecondLocalKey;
|
|
59
|
+
secondKey: SecondKey;
|
|
60
|
+
throughParentKey?: string;
|
|
61
|
+
}): HasManyThroughRelation<TParent, TFar, LocalKey, FirstKey, SecondLocalKey, SecondKey>;
|
|
43
62
|
declare function belongsToMany<TParent, TRelated, Pivot extends object, ParentKey extends keyof TParent & string = keyof TParent & string, RelatedKey extends keyof TRelated & string = keyof TRelated & string, ForeignPivotKey extends keyof Pivot & string = keyof Pivot & string, RelatedPivotKey extends keyof Pivot & string = keyof Pivot & string>(definition: {
|
|
44
63
|
name: string;
|
|
45
64
|
pivotTable: string;
|
|
@@ -48,6 +67,8 @@ declare function belongsToMany<TParent, TRelated, Pivot extends object, ParentKe
|
|
|
48
67
|
foreignPivotKey: ForeignPivotKey;
|
|
49
68
|
relatedPivotKey: RelatedPivotKey;
|
|
50
69
|
}): BelongsToManyRelation<TParent, TRelated, Pivot, ParentKey, RelatedKey, ForeignPivotKey, RelatedPivotKey>;
|
|
70
|
+
declare function relationMatchKey(value: unknown): string;
|
|
71
|
+
declare function getByRelationKey<K, V>(map: ReadonlyMap<K, V>, key: unknown): V | undefined;
|
|
51
72
|
declare function indexHasManyRelation<TParent, TChild, LocalKey extends keyof TParent & string, ForeignKey extends keyof TChild & string>(parents: readonly TParent[], children: readonly TChild[], relation: HasManyRelation<TParent, TChild, LocalKey, ForeignKey>): Map<TParent[LocalKey], TChild[]>;
|
|
52
73
|
declare function indexHasOneRelation<TParent, TChild, LocalKey extends keyof TParent & string, ForeignKey extends keyof TChild & string>(parents: readonly TParent[], children: readonly TChild[], relation: HasOneRelation<TParent, TChild, LocalKey, ForeignKey>): Map<TParent[LocalKey], TChild | undefined>;
|
|
53
74
|
declare function indexBelongsToRelation<TChild, TParent, ForeignKey extends keyof TChild & string, OwnerKey extends keyof TParent & string>(children: readonly TChild[], parents: readonly TParent[], relation: BelongsToRelation<TChild, TParent, ForeignKey, OwnerKey>): Map<TChild[ForeignKey], TParent>;
|
|
@@ -95,6 +116,7 @@ declare function morphTo<TChild, MorphTypeKey extends keyof TChild & string = ke
|
|
|
95
116
|
}): MorphToRelation<TChild, MorphTypeKey, MorphIdKey>;
|
|
96
117
|
declare function indexMorphManyRelation<TParent, TChild, LocalKey extends keyof TParent & string, MorphTypeKey extends keyof TChild & string, MorphIdKey extends keyof TChild & string>(parents: readonly TParent[], children: readonly TChild[], relation: MorphManyRelation<TParent, TChild, LocalKey, MorphTypeKey, MorphIdKey>): Map<TParent[LocalKey], TChild[]>;
|
|
97
118
|
declare function indexMorphOneRelation<TParent, TChild, LocalKey extends keyof TParent & string, MorphTypeKey extends keyof TChild & string, MorphIdKey extends keyof TChild & string>(parents: readonly TParent[], children: readonly TChild[], relation: MorphOneRelation<TParent, TChild, LocalKey, MorphTypeKey, MorphIdKey>): Map<TParent[LocalKey], TChild | undefined>;
|
|
98
|
-
declare function
|
|
99
|
-
|
|
100
|
-
export {
|
|
119
|
+
declare function indexHasManyThroughRelation<TParent, TFar, LocalKey extends keyof TParent & string, FirstKey extends string, SecondLocalKey extends string, SecondKey extends keyof TFar & string>(parents: readonly TParent[], children: readonly (TFar & Record<string, unknown>)[], relation: HasManyThroughRelation<TParent, TFar, LocalKey, FirstKey, SecondLocalKey, SecondKey>): Map<TParent[LocalKey], TFar[]>;
|
|
120
|
+
declare function indexMorphToRelation<TChild, TParent extends object, MorphTypeKey extends keyof TChild & string, MorphIdKey extends keyof TChild & string, _OwnerKey extends keyof TParent & string = keyof TParent & string>(children: readonly TChild[], parentsByType: ReadonlyMap<string, ReadonlyMap<unknown, TParent>>, relation: MorphToRelation<TChild, MorphTypeKey, MorphIdKey>): Map<TChild[MorphIdKey], TParent>;
|
|
121
|
+
export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, };
|
|
122
|
+
export { belongsTo, belongsToMany, getByRelationKey, hasMany, hasManyThrough, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasManyThroughRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, relationMatchKey, };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PaginatedResult } from "../pagination/index.ts";
|
|
2
2
|
import type BaseRepository from "./baseRepository.ts";
|
|
3
|
-
import type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, MorphManyRelation, MorphOneRelation, MorphToRelation } from "./relationships.ts";
|
|
3
|
+
import type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, MorphManyRelation, MorphOneRelation, MorphToRelation } from "./relationships.ts";
|
|
4
4
|
import type { QueryOptions, QueryWhere } from "./types.ts";
|
|
5
5
|
import { WhereBuilder } from "./whereBuilder.ts";
|
|
6
6
|
type LoadedRow = Record<string, unknown>;
|
|
@@ -31,6 +31,9 @@ declare class RepositoryQuery<TEntity extends object, PrimaryKey extends keyof T
|
|
|
31
31
|
withMorphOne<TChild extends object, LocalKey extends keyof TEntity & string, MorphTypeKey extends keyof TChild & string, MorphIdKey extends keyof TChild & string, Alias extends string>(as: Alias, relation: MorphOneRelation<TEntity, TChild, LocalKey, MorphTypeKey, MorphIdKey>, childRepository: BaseRepository<TChild, keyof TChild & string>, options?: Omit<QueryOptions<TChild>, "where">): this;
|
|
32
32
|
withMorphTo<TParent extends object, MorphTypeKey extends keyof TEntity & string, MorphIdKey extends keyof TEntity & string, Alias extends string>(as: Alias, relation: MorphToRelation<TEntity, MorphTypeKey, MorphIdKey>, repositoriesByType: ReadonlyMap<string, BaseRepository<TParent, keyof TParent & string>>, options?: Omit<QueryOptions<TParent>, "where">): this;
|
|
33
33
|
withBelongsToMany<TRelated extends object, Pivot extends object, ParentKey extends keyof TEntity & string, RelatedKey extends keyof TRelated & string, ForeignPivotKey extends keyof Pivot & string, RelatedPivotKey extends keyof Pivot & string, Alias extends string>(as: Alias, relation: BelongsToManyRelation<TEntity, TRelated, Pivot, ParentKey, RelatedKey, ForeignPivotKey, RelatedPivotKey>, relatedRepository: BaseRepository<TRelated, RelatedKey>, options?: Omit<QueryOptions<TRelated>, "where">): this;
|
|
34
|
+
withHasManyThrough<TFar extends object, LocalKey extends keyof TEntity & string, SecondKey extends keyof TFar & string, Alias extends string>(as: Alias, relation: HasManyThroughRelation<TEntity, TFar, LocalKey, string, string, SecondKey>, farRepository: BaseRepository<TFar, keyof TFar & string>, options?: Omit<QueryOptions<TFar>, "where">): this;
|
|
35
|
+
withTrashed(): this;
|
|
36
|
+
onlyTrashed(): this;
|
|
34
37
|
get(): Promise<Array<TEntity & LoadedRow>>;
|
|
35
38
|
first(): Promise<(TEntity & LoadedRow) | null>;
|
|
36
39
|
count(): Promise<number>;
|
|
@@ -775,7 +775,8 @@ class BelongsToManyRelationQuery {
|
|
|
775
775
|
return;
|
|
776
776
|
}
|
|
777
777
|
const list = Array.isArray(ids) ? ids : [ids];
|
|
778
|
-
|
|
778
|
+
const placeholders = list.map((_, index) => `$${index + 2}`).join(", ");
|
|
779
|
+
await this.connection().unsafe(`DELETE FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1 AND ${String(this.relation.relatedPivotKey)} IN (${placeholders})`, [parentId, ...list]);
|
|
779
780
|
}
|
|
780
781
|
async sync(ids) {
|
|
781
782
|
await this.detach();
|
|
@@ -957,6 +958,65 @@ class MorphToRelationQuery {
|
|
|
957
958
|
}
|
|
958
959
|
}
|
|
959
960
|
|
|
961
|
+
class HasManyThroughRelationQuery {
|
|
962
|
+
parent;
|
|
963
|
+
related;
|
|
964
|
+
relation;
|
|
965
|
+
kind = "hasManyThrough";
|
|
966
|
+
extraWhere = {};
|
|
967
|
+
extraOptions = {};
|
|
968
|
+
constructor(parent, related, relation) {
|
|
969
|
+
this.parent = parent;
|
|
970
|
+
this.related = related;
|
|
971
|
+
this.relation = relation;
|
|
972
|
+
}
|
|
973
|
+
where(where) {
|
|
974
|
+
this.extraWhere = { ...this.extraWhere, ...where };
|
|
975
|
+
return this;
|
|
976
|
+
}
|
|
977
|
+
orderBy(orderBy) {
|
|
978
|
+
this.extraOptions = { ...this.extraOptions, orderBy };
|
|
979
|
+
return this;
|
|
980
|
+
}
|
|
981
|
+
limit(limit) {
|
|
982
|
+
this.extraOptions = { ...this.extraOptions, limit };
|
|
983
|
+
return this;
|
|
984
|
+
}
|
|
985
|
+
applyEagerLoad(query, alias) {
|
|
986
|
+
query.withHasManyThrough(alias, this.relation, this.related.repository(), this.extraOptions);
|
|
987
|
+
}
|
|
988
|
+
hydrateEager(row, alias) {
|
|
989
|
+
const value = row[alias] ?? [];
|
|
990
|
+
const rows = Array.isArray(value) ? value : [];
|
|
991
|
+
return rows.map((item) => this.related.newFromRecord(item));
|
|
992
|
+
}
|
|
993
|
+
toExistsClause(parentTable) {
|
|
994
|
+
const farTable = this.related.repository().getTable().name;
|
|
995
|
+
const extra = buildAdvancedWhereClause(farTable, this.extraWhere, [], []);
|
|
996
|
+
const extraSql = extra.clause.replace(/^ WHERE /, "");
|
|
997
|
+
const sql = `SELECT 1 FROM ${quoteIdentifier(farTable)} INNER JOIN ${quoteIdentifier(this.relation.throughTable)} ON ${qualifyColumn(this.relation.throughTable, this.relation.secondLocalKey)} = ${qualifyColumn(farTable, this.relation.secondKey)} WHERE ${qualifyColumn(this.relation.throughTable, this.relation.firstKey)} = ${qualifyColumn(parentTable, this.relation.localKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
|
|
998
|
+
return { sql, params: extra.params };
|
|
999
|
+
}
|
|
1000
|
+
async get() {
|
|
1001
|
+
const rows = await this.related.repository().withConnection(this.parent.getRepository().getConnection()).findHasManyThrough(this.parent.get(this.relation.localKey), this.relation, {
|
|
1002
|
+
...this.extraOptions,
|
|
1003
|
+
where: this.extraWhere
|
|
1004
|
+
});
|
|
1005
|
+
return rows.map((row) => this.related.newFromRecord(row));
|
|
1006
|
+
}
|
|
1007
|
+
async first() {
|
|
1008
|
+
const rows = await this.limit(1).get();
|
|
1009
|
+
return rows[0] ?? null;
|
|
1010
|
+
}
|
|
1011
|
+
async count() {
|
|
1012
|
+
const rows = await this.get();
|
|
1013
|
+
return rows.length;
|
|
1014
|
+
}
|
|
1015
|
+
then(onfulfilled, onrejected) {
|
|
1016
|
+
return thenGet(() => this.get(), onfulfilled, onrejected);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
960
1020
|
// ../../src/core/database/relationships.ts
|
|
961
1021
|
function hasMany(definition) {
|
|
962
1022
|
return {
|
|
@@ -976,32 +1036,80 @@ function belongsTo(definition) {
|
|
|
976
1036
|
...definition
|
|
977
1037
|
};
|
|
978
1038
|
}
|
|
1039
|
+
function hasManyThrough(definition) {
|
|
1040
|
+
return {
|
|
1041
|
+
type: "hasManyThrough",
|
|
1042
|
+
throughParentKey: "__through_parent_id",
|
|
1043
|
+
...definition
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
979
1046
|
function belongsToMany(definition) {
|
|
980
1047
|
return {
|
|
981
1048
|
type: "belongsToMany",
|
|
982
1049
|
...definition
|
|
983
1050
|
};
|
|
984
1051
|
}
|
|
1052
|
+
function relationMatchKey(value) {
|
|
1053
|
+
if (value === null || value === undefined) {
|
|
1054
|
+
return "";
|
|
1055
|
+
}
|
|
1056
|
+
if (typeof value === "bigint") {
|
|
1057
|
+
return value.toString();
|
|
1058
|
+
}
|
|
1059
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1060
|
+
return String(value);
|
|
1061
|
+
}
|
|
1062
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) {
|
|
1063
|
+
return BigInt(value).toString();
|
|
1064
|
+
}
|
|
1065
|
+
return String(value);
|
|
1066
|
+
}
|
|
1067
|
+
function getByRelationKey(map, key) {
|
|
1068
|
+
if (map.has(key)) {
|
|
1069
|
+
return map.get(key);
|
|
1070
|
+
}
|
|
1071
|
+
const want = relationMatchKey(key);
|
|
1072
|
+
if (want === "") {
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
for (const [existing, value] of map) {
|
|
1076
|
+
if (relationMatchKey(existing) === want) {
|
|
1077
|
+
return value;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
985
1082
|
function indexHasManyRelation(parents, children, relation) {
|
|
986
1083
|
const groups = new Map;
|
|
1084
|
+
const originalKeys = new Map;
|
|
987
1085
|
for (const parent of parents) {
|
|
988
|
-
|
|
1086
|
+
const key = relationMatchKey(parent[relation.localKey]);
|
|
1087
|
+
if (!groups.has(key)) {
|
|
1088
|
+
groups.set(key, []);
|
|
1089
|
+
originalKeys.set(key, parent[relation.localKey]);
|
|
1090
|
+
}
|
|
989
1091
|
}
|
|
990
1092
|
for (const child of children) {
|
|
991
|
-
const
|
|
992
|
-
const group = groups.get(key);
|
|
1093
|
+
const group = groups.get(relationMatchKey(child[relation.foreignKey]));
|
|
993
1094
|
if (!group) {
|
|
994
1095
|
continue;
|
|
995
1096
|
}
|
|
996
1097
|
group.push(child);
|
|
997
1098
|
}
|
|
998
|
-
|
|
1099
|
+
const result = new Map;
|
|
1100
|
+
for (const [key, group] of groups) {
|
|
1101
|
+
const original = originalKeys.get(key);
|
|
1102
|
+
if (original !== undefined) {
|
|
1103
|
+
result.set(original, group);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return result;
|
|
999
1107
|
}
|
|
1000
1108
|
function indexHasOneRelation(parents, children, relation) {
|
|
1001
1109
|
const grouped = indexHasManyRelation(parents, children, relation);
|
|
1002
1110
|
const result = new Map;
|
|
1003
1111
|
for (const parent of parents) {
|
|
1004
|
-
const matches = grouped
|
|
1112
|
+
const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
|
|
1005
1113
|
result.set(parent[relation.localKey], matches[0]);
|
|
1006
1114
|
}
|
|
1007
1115
|
return result;
|
|
@@ -1009,12 +1117,12 @@ function indexHasOneRelation(parents, children, relation) {
|
|
|
1009
1117
|
function indexBelongsToRelation(children, parents, relation) {
|
|
1010
1118
|
const parentsById = new Map;
|
|
1011
1119
|
for (const parent of parents) {
|
|
1012
|
-
parentsById.set(parent[relation.ownerKey], parent);
|
|
1120
|
+
parentsById.set(relationMatchKey(parent[relation.ownerKey]), parent);
|
|
1013
1121
|
}
|
|
1014
1122
|
const result = new Map;
|
|
1015
1123
|
for (const child of children) {
|
|
1016
1124
|
const foreignKey = child[relation.foreignKey];
|
|
1017
|
-
const parent = parentsById.get(foreignKey);
|
|
1125
|
+
const parent = parentsById.get(relationMatchKey(foreignKey));
|
|
1018
1126
|
if (parent) {
|
|
1019
1127
|
result.set(foreignKey, parent);
|
|
1020
1128
|
}
|
|
@@ -1024,23 +1132,33 @@ function indexBelongsToRelation(children, parents, relation) {
|
|
|
1024
1132
|
function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
|
|
1025
1133
|
const relatedById = new Map;
|
|
1026
1134
|
for (const related of relatedRows) {
|
|
1027
|
-
relatedById.set(related[relation.relatedKey], related);
|
|
1135
|
+
relatedById.set(relationMatchKey(related[relation.relatedKey]), related);
|
|
1028
1136
|
}
|
|
1029
1137
|
const groups = new Map;
|
|
1138
|
+
const originalKeys = new Map;
|
|
1030
1139
|
for (const parent of parents) {
|
|
1031
|
-
|
|
1140
|
+
const key = relationMatchKey(parent[relation.parentKey]);
|
|
1141
|
+
if (!groups.has(key)) {
|
|
1142
|
+
groups.set(key, []);
|
|
1143
|
+
originalKeys.set(key, parent[relation.parentKey]);
|
|
1144
|
+
}
|
|
1032
1145
|
}
|
|
1033
1146
|
for (const pivot of pivotRows) {
|
|
1034
|
-
const
|
|
1035
|
-
const
|
|
1036
|
-
const group = groups.get(parentId);
|
|
1037
|
-
const related = relatedById.get(relatedId);
|
|
1147
|
+
const group = groups.get(relationMatchKey(pivot[relation.foreignPivotKey]));
|
|
1148
|
+
const related = relatedById.get(relationMatchKey(pivot[relation.relatedPivotKey]));
|
|
1038
1149
|
if (!group || !related) {
|
|
1039
1150
|
continue;
|
|
1040
1151
|
}
|
|
1041
1152
|
group.push(related);
|
|
1042
1153
|
}
|
|
1043
|
-
|
|
1154
|
+
const result = new Map;
|
|
1155
|
+
for (const [key, group] of groups) {
|
|
1156
|
+
const original = originalKeys.get(key);
|
|
1157
|
+
if (original !== undefined) {
|
|
1158
|
+
result.set(original, group);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return result;
|
|
1044
1162
|
}
|
|
1045
1163
|
function morphMany(definition) {
|
|
1046
1164
|
return {
|
|
@@ -1062,31 +1180,62 @@ function morphTo(definition) {
|
|
|
1062
1180
|
}
|
|
1063
1181
|
function indexMorphManyRelation(parents, children, relation) {
|
|
1064
1182
|
const groups = new Map;
|
|
1183
|
+
const originalKeys = new Map;
|
|
1065
1184
|
for (const parent of parents) {
|
|
1066
|
-
|
|
1185
|
+
const key = relationMatchKey(parent[relation.localKey]);
|
|
1186
|
+
if (!groups.has(key)) {
|
|
1187
|
+
groups.set(key, []);
|
|
1188
|
+
originalKeys.set(key, parent[relation.localKey]);
|
|
1189
|
+
}
|
|
1067
1190
|
}
|
|
1068
1191
|
for (const child of children) {
|
|
1069
1192
|
if (child[relation.morphTypeKey] !== relation.morphType) {
|
|
1070
1193
|
continue;
|
|
1071
1194
|
}
|
|
1072
|
-
const
|
|
1073
|
-
const group = groups.get(key);
|
|
1195
|
+
const group = groups.get(relationMatchKey(child[relation.morphIdKey]));
|
|
1074
1196
|
if (!group) {
|
|
1075
1197
|
continue;
|
|
1076
1198
|
}
|
|
1077
1199
|
group.push(child);
|
|
1078
1200
|
}
|
|
1079
|
-
|
|
1201
|
+
const result = new Map;
|
|
1202
|
+
for (const [key, group] of groups) {
|
|
1203
|
+
const original = originalKeys.get(key);
|
|
1204
|
+
if (original !== undefined) {
|
|
1205
|
+
result.set(original, group);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return result;
|
|
1080
1209
|
}
|
|
1081
1210
|
function indexMorphOneRelation(parents, children, relation) {
|
|
1082
1211
|
const grouped = indexMorphManyRelation(parents, children, relation);
|
|
1083
1212
|
const result = new Map;
|
|
1084
1213
|
for (const parent of parents) {
|
|
1085
|
-
const matches = grouped
|
|
1214
|
+
const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
|
|
1086
1215
|
result.set(parent[relation.localKey], matches[0]);
|
|
1087
1216
|
}
|
|
1088
1217
|
return result;
|
|
1089
1218
|
}
|
|
1219
|
+
function indexHasManyThroughRelation(parents, children, relation) {
|
|
1220
|
+
const throughKey = relation.throughParentKey ?? "__through_parent_id";
|
|
1221
|
+
const grouped = new Map;
|
|
1222
|
+
for (const child of children) {
|
|
1223
|
+
const key = relationMatchKey(child[throughKey]);
|
|
1224
|
+
if (key === "") {
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
const existing = grouped.get(key) ?? [];
|
|
1228
|
+
const { [throughKey]: _through, ...far } = child;
|
|
1229
|
+
existing.push(far);
|
|
1230
|
+
grouped.set(key, existing);
|
|
1231
|
+
}
|
|
1232
|
+
const result = new Map;
|
|
1233
|
+
for (const parent of parents) {
|
|
1234
|
+
const key = relationMatchKey(parent[relation.localKey]);
|
|
1235
|
+
result.set(parent[relation.localKey], grouped.get(key) ?? []);
|
|
1236
|
+
}
|
|
1237
|
+
return result;
|
|
1238
|
+
}
|
|
1090
1239
|
function indexMorphToRelation(children, parentsByType, relation) {
|
|
1091
1240
|
const result = new Map;
|
|
1092
1241
|
for (const child of children) {
|
|
@@ -1095,7 +1244,7 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
1095
1244
|
if (!parents) {
|
|
1096
1245
|
continue;
|
|
1097
1246
|
}
|
|
1098
|
-
const parent = parents
|
|
1247
|
+
const parent = getByRelationKey(parents, child[relation.morphIdKey]);
|
|
1099
1248
|
if (parent) {
|
|
1100
1249
|
result.set(child[relation.morphIdKey], parent);
|
|
1101
1250
|
}
|
|
@@ -1431,6 +1580,18 @@ class ModelQuery {
|
|
|
1431
1580
|
this.query.withMorphTo(...args);
|
|
1432
1581
|
return this;
|
|
1433
1582
|
}
|
|
1583
|
+
withHasManyThrough(...args) {
|
|
1584
|
+
this.query.withHasManyThrough(...args);
|
|
1585
|
+
return this;
|
|
1586
|
+
}
|
|
1587
|
+
withTrashed() {
|
|
1588
|
+
this.query.withTrashed();
|
|
1589
|
+
return this;
|
|
1590
|
+
}
|
|
1591
|
+
onlyTrashed() {
|
|
1592
|
+
this.query.onlyTrashed();
|
|
1593
|
+
return this;
|
|
1594
|
+
}
|
|
1434
1595
|
async get() {
|
|
1435
1596
|
const statics = modelStatics(this.modelClass);
|
|
1436
1597
|
const rows = await this.query.get();
|
|
@@ -1638,6 +1799,31 @@ class Model {
|
|
|
1638
1799
|
static with(...relations) {
|
|
1639
1800
|
return Model.query.call(this).with(...relations);
|
|
1640
1801
|
}
|
|
1802
|
+
static withTrashed() {
|
|
1803
|
+
return Model.query.call(this).withTrashed();
|
|
1804
|
+
}
|
|
1805
|
+
static onlyTrashed() {
|
|
1806
|
+
return Model.query.call(this).onlyTrashed();
|
|
1807
|
+
}
|
|
1808
|
+
static async chunk(count, callback) {
|
|
1809
|
+
const statics = modelStatics(this);
|
|
1810
|
+
ensureBooted(this);
|
|
1811
|
+
await resolveModelRepository(this).chunk(count, async (rows) => {
|
|
1812
|
+
return await callback(rows.map((row) => statics.newFromRecord(row, true)));
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
static async cursorPaginate(options) {
|
|
1816
|
+
const statics = modelStatics(this);
|
|
1817
|
+
ensureBooted(this);
|
|
1818
|
+
const page = await resolveModelRepository(this).cursorPaginate({
|
|
1819
|
+
perPage: options.perPage,
|
|
1820
|
+
cursor: options.cursor
|
|
1821
|
+
});
|
|
1822
|
+
return {
|
|
1823
|
+
data: page.data.map((row) => statics.newFromRecord(row, true)),
|
|
1824
|
+
meta: page.meta
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1641
1827
|
static whereHas(name, constrain) {
|
|
1642
1828
|
return Model.query.call(this).whereHas(name, constrain);
|
|
1643
1829
|
}
|
|
@@ -1762,7 +1948,7 @@ class Model {
|
|
|
1762
1948
|
}
|
|
1763
1949
|
async loadHasMany(as, relation, childRepository, options = {}) {
|
|
1764
1950
|
const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
|
|
1765
|
-
const loaded = grouped
|
|
1951
|
+
const loaded = getByRelationKey(grouped, this.attributes[relation.localKey]) ?? [];
|
|
1766
1952
|
return Object.assign(this, { [as]: loaded });
|
|
1767
1953
|
}
|
|
1768
1954
|
async loadHasOne(as, relation, childRepository, options = {}) {
|
|
@@ -1772,7 +1958,7 @@ class Model {
|
|
|
1772
1958
|
}
|
|
1773
1959
|
async loadBelongsTo(as, relation, parentRepository, options = {}) {
|
|
1774
1960
|
const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
|
|
1775
|
-
const loaded = grouped
|
|
1961
|
+
const loaded = getByRelationKey(grouped, this.attributes[relation.foreignKey]);
|
|
1776
1962
|
return Object.assign(this, { [as]: loaded });
|
|
1777
1963
|
}
|
|
1778
1964
|
async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
|
|
@@ -1792,7 +1978,7 @@ class Model {
|
|
|
1792
1978
|
}
|
|
1793
1979
|
});
|
|
1794
1980
|
const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
|
|
1795
|
-
const loaded = grouped
|
|
1981
|
+
const loaded = getByRelationKey(grouped, parentId) ?? [];
|
|
1796
1982
|
return Object.assign(this, { [as]: loaded });
|
|
1797
1983
|
}
|
|
1798
1984
|
hasMany(related, foreignKey, localKey) {
|
|
@@ -1822,6 +2008,20 @@ class Model {
|
|
|
1822
2008
|
ownerKey: ownerKey ?? relatedTable.primaryKey
|
|
1823
2009
|
}));
|
|
1824
2010
|
}
|
|
2011
|
+
hasManyThrough(related, through, firstKey, secondKey, localKey, secondLocalKey) {
|
|
2012
|
+
const table = this.repository.getTable();
|
|
2013
|
+
const relatedClass = resolveRelated(related);
|
|
2014
|
+
const throughClass = resolveRelated(through);
|
|
2015
|
+
const throughTable = throughClass.repository().getTable();
|
|
2016
|
+
return new HasManyThroughRelationQuery(this, relatedClass, hasManyThrough({
|
|
2017
|
+
name: relatedClass.repository().getTable().name,
|
|
2018
|
+
throughTable: throughTable.name,
|
|
2019
|
+
localKey: localKey ?? table.primaryKey,
|
|
2020
|
+
firstKey: firstKey ?? foreignKeyFromTable(table.name),
|
|
2021
|
+
secondLocalKey: secondLocalKey ?? throughTable.primaryKey,
|
|
2022
|
+
secondKey: secondKey ?? foreignKeyFromTable(throughTable.name)
|
|
2023
|
+
}));
|
|
2024
|
+
}
|
|
1825
2025
|
belongsToMany(related, pivotTable, foreignPivotKey, relatedPivotKey) {
|
|
1826
2026
|
const table = this.repository.getTable();
|
|
1827
2027
|
const relatedClass = resolveRelated(related);
|
|
@@ -1913,6 +2113,7 @@ export {
|
|
|
1913
2113
|
BelongsToManyRelationQuery,
|
|
1914
2114
|
BelongsToRelationQuery,
|
|
1915
2115
|
HasManyRelationQuery,
|
|
2116
|
+
HasManyThroughRelationQuery,
|
|
1916
2117
|
HasOneRelationQuery,
|
|
1917
2118
|
Model,
|
|
1918
2119
|
ModelQuery,
|