public commit

This commit is contained in:
dswbx
2024-11-16 12:01:47 +01:00
commit 90f80c4280
582 changed files with 49291 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
import { type Static, Type } from "core/utils";
import type { Entity, EntityManager } from "../entities";
import { NumberField, TextField } from "../fields";
import type { RepoQuery } from "../server/data-query-impl";
import { EntityRelation, type KyselyJsonFrom, type KyselyQueryBuilder } from "./EntityRelation";
import { EntityRelationAnchor } from "./EntityRelationAnchor";
import { type RelationType, RelationTypes } from "./relation-types";
export type PolymorphicRelationConfig = Static<typeof PolymorphicRelation.schema>;
// @todo: what about cascades?
export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelation.schema> {
static override schema = Type.Composite(
[
EntityRelation.schema,
Type.Object({
targetCardinality: Type.Optional(Type.Number())
})
],
{
additionalProperties: false
}
);
constructor(source: Entity, target: Entity, config: Partial<PolymorphicRelationConfig> = {}) {
const mappedBy = config.mappedBy || target.name;
const inversedBy = config.inversedBy || source.name;
// if target can be multiple, allow it. otherwise unlimited
const targetCardinality =
typeof config.targetCardinality === "number" && config.targetCardinality > 0
? config.targetCardinality
: undefined;
const sourceAnchor = new EntityRelationAnchor(source, inversedBy, 1);
const targetAnchor = new EntityRelationAnchor(target, mappedBy, targetCardinality);
super(sourceAnchor, targetAnchor, config);
this.directions = ["source"];
}
type(): RelationType {
return RelationTypes.Polymorphic;
}
private queryInfo(entity: Entity) {
const other = this.other(entity);
const whereLhs = `${other.entity.name}.${this.getReferenceField().name}`;
const reference = `${entity.name}.${this.config.mappedBy}`;
// this is used for "getReferenceQuery"
const reference_other = `${other.entity.name}.${this.config.mappedBy}`;
const entityRef = `${entity.name}.${entity.getPrimaryField().name}`;
const otherRef = `${other.entity.name}.${this.getEntityIdField().name}`;
const groupBy = `${entity.name}.${entity.getPrimaryField().name}`;
return {
other,
whereLhs,
reference,
reference_other,
entityRef,
otherRef,
groupBy
};
}
buildJoin(entity: Entity, qb: KyselyQueryBuilder) {
const { other, whereLhs, reference, entityRef, otherRef, groupBy } = this.queryInfo(entity);
return qb
.innerJoin(other.entity.name, (join) =>
join.onRef(entityRef, "=", otherRef).on(whereLhs, "=", reference)
)
.groupBy(groupBy);
}
override getReferenceQuery(entity: Entity, id: number): Partial<RepoQuery> {
const info = this.queryInfo(entity);
return {
where: {
[this.getReferenceField().name]: info.reference_other,
[this.getEntityIdField().name]: id
}
};
}
buildWith(entity: Entity, qb: KyselyQueryBuilder, jsonFrom: KyselyJsonFrom) {
const { other, whereLhs, reference, entityRef, otherRef } = this.queryInfo(entity);
const limit = other.cardinality === 1 ? 1 : 5;
return qb.select((eb) =>
jsonFrom(
eb
.selectFrom(other.entity.name)
.select(other.entity.getSelect(other.entity.name))
.where(whereLhs, "=", reference)
.whereRef(entityRef, "=", otherRef)
.limit(limit)
).as(other.reference)
);
}
override isListableFor(entity: Entity): boolean {
// @todo: only make listable if many? check cardinality
return this.source.entity.name === entity.name && this.target.cardinality !== 1;
}
getReferenceField(): TextField {
return new TextField("reference", { hidden: true, fillable: ["create"] });
}
getEntityIdField(): NumberField {
return new NumberField("entity_id", { hidden: true, fillable: ["create"] });
}
initialize(em: EntityManager<any>) {
const referenceField = this.getReferenceField();
const entityIdField = this.getEntityIdField();
if (!this.target.entity.field(referenceField.name)) {
this.target.entity.addField(referenceField);
}
if (!this.target.entity.field(entityIdField.name)) {
this.target.entity.addField(entityIdField);
}
}
}