|
@@ -8,103 +8,150 @@ export default class Model {
|
|
|
|
|
|
private _uses: number;
|
|
|
|
|
|
+ private _loadedRelations: string[];
|
|
|
+
|
|
|
constructor(data: object) {
|
|
|
this._uses = 0;
|
|
|
+ this._loadedRelations = [];
|
|
|
|
|
|
Object.assign(this, data);
|
|
|
}
|
|
|
|
|
|
- private async _loadRelation(relation: object): Promise<object> {
|
|
|
- const { findById, registerModels } = useModelStore();
|
|
|
-
|
|
|
- const data = await findById(relation._name, relation._id);
|
|
|
-
|
|
|
- const [model] = await registerModels(data);
|
|
|
-
|
|
|
- return model;
|
|
|
- }
|
|
|
-
|
|
|
- private async _loadRelations(model: object): Promise<object> {
|
|
|
- const relations = Object.fromEntries(
|
|
|
- await Promise.all(
|
|
|
- Object.entries(model)
|
|
|
- .filter(
|
|
|
- ([, value]) =>
|
|
|
- typeof value === "object" || Array.isArray(value)
|
|
|
- )
|
|
|
- .map(async ([key, value]) => {
|
|
|
- if (
|
|
|
- typeof value === "object" &&
|
|
|
- value._id &&
|
|
|
- value._name
|
|
|
- )
|
|
|
- return [key, await this._loadRelation(value)];
|
|
|
-
|
|
|
- if (Array.isArray(value))
|
|
|
- return [
|
|
|
- key,
|
|
|
- await Promise.all(
|
|
|
- value.map(async item => {
|
|
|
- if (typeof item !== "object")
|
|
|
- return item;
|
|
|
-
|
|
|
- if (item._id && item._name)
|
|
|
- return this._loadRelation(item);
|
|
|
-
|
|
|
- return this._loadRelations(item);
|
|
|
- })
|
|
|
- )
|
|
|
- ];
|
|
|
-
|
|
|
- return [key, await this._loadRelations(value)];
|
|
|
- })
|
|
|
+ private async _getRelations(
|
|
|
+ model?: object,
|
|
|
+ path?: string
|
|
|
+ ): Promise<string[]> {
|
|
|
+ const relationPaths = await Object.entries(model ?? this)
|
|
|
+ .filter(
|
|
|
+ ([key, value]) =>
|
|
|
+ !key.startsWith("_") &&
|
|
|
+ (typeof value === "object" || Array.isArray(value))
|
|
|
)
|
|
|
- );
|
|
|
+ .reduce(async (_modelIds, [key, value]) => {
|
|
|
+ const paths = await _modelIds;
|
|
|
|
|
|
- Object.assign(model, relations);
|
|
|
+ path = path ? `${path}.${key}` : key;
|
|
|
|
|
|
- return model;
|
|
|
- }
|
|
|
-
|
|
|
- public async loadRelations(): Promise<void> {
|
|
|
- await this._loadRelations(this);
|
|
|
- }
|
|
|
-
|
|
|
- private async _getRelationIds(model: object): Promise<string[]> {
|
|
|
- const relationIds = await Object.values(model)
|
|
|
- .filter(value => typeof value === "object" || Array.isArray(value))
|
|
|
- .reduce(async (_modelIds, value) => {
|
|
|
- const modelIds = await _modelIds;
|
|
|
-
|
|
|
- if (typeof value === "object" && value._id)
|
|
|
- modelIds.push(value._id);
|
|
|
+ if (typeof value === "object" && value._id) paths.push(path);
|
|
|
else if (Array.isArray(value))
|
|
|
await Promise.all(
|
|
|
value.map(async item => {
|
|
|
if (typeof item !== "object") return;
|
|
|
|
|
|
- if (item._id) modelIds.push(item._id);
|
|
|
+ if (item._id) paths.push(path);
|
|
|
else
|
|
|
- modelIds.push(
|
|
|
- ...(await this._getRelationIds(item))
|
|
|
+ paths.push(
|
|
|
+ ...(await this._getRelations(item, path))
|
|
|
);
|
|
|
})
|
|
|
);
|
|
|
- else modelIds.push(...(await this._getRelationIds(value)));
|
|
|
+ else paths.push(...(await this._getRelations(value, path)));
|
|
|
|
|
|
- return modelIds;
|
|
|
+ return paths;
|
|
|
}, Promise.resolve([]));
|
|
|
|
|
|
- return relationIds.filter(
|
|
|
- (relationId, index) => relationIds.indexOf(relationId) === index
|
|
|
+ return relationPaths.filter(
|
|
|
+ (relationPath, index) =>
|
|
|
+ relationPaths.indexOf(relationPath) === index
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- public async unregisterRelations(): Promise<void> {
|
|
|
- const { unregisterModels } = useModelStore();
|
|
|
+ private async _getRelation(key: string) {
|
|
|
+ let relation = JSON.parse(JSON.stringify(this));
|
|
|
+ key.split(".").forEach(property => {
|
|
|
+ if (Number.isInteger(property))
|
|
|
+ property = Number.parseInt(property);
|
|
|
+
|
|
|
+ relation = relation[property];
|
|
|
+ });
|
|
|
+ return relation;
|
|
|
+ }
|
|
|
+
|
|
|
+ private async _loadRelation(
|
|
|
+ model: object,
|
|
|
+ path: string,
|
|
|
+ force: boolean,
|
|
|
+ pathParts?: string[]
|
|
|
+ ): Promise<void> {
|
|
|
+ let [head, ...rest] = path.split(".");
|
|
|
+ let [next] = rest;
|
|
|
+
|
|
|
+ if (Number.isInteger(head)) head = Number.parseInt(head);
|
|
|
+
|
|
|
+ if (Number.isInteger(next)) next = Number.parseInt(next);
|
|
|
+
|
|
|
+ pathParts ??= [];
|
|
|
|
|
|
- const relationIds = await this._getRelationIds(this);
|
|
|
+ pathParts.push(head);
|
|
|
|
|
|
+ if (Array.isArray(model[head])) {
|
|
|
+ await Promise.all(
|
|
|
+ model[head].map(async (item, index) => {
|
|
|
+ let itemPath = `${index}`;
|
|
|
+
|
|
|
+ if (rest.length > 0) itemPath += `.${rest.join(".")}`;
|
|
|
+
|
|
|
+ await this._loadRelation(model[head], itemPath, force, [
|
|
|
+ ...pathParts
|
|
|
+ ]);
|
|
|
+ })
|
|
|
+ );
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rest.length > 0 && model[next] === null) {
|
|
|
+ await this._loadRelation(
|
|
|
+ model[head],
|
|
|
+ rest.join("."),
|
|
|
+ force,
|
|
|
+ pathParts
|
|
|
+ );
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const fullPath = pathParts.join(".");
|
|
|
+
|
|
|
+ if (force || !this._loadedRelations.includes(fullPath)) {
|
|
|
+ const { findById, registerModels } = useModelStore();
|
|
|
+
|
|
|
+ const data = await findById(model[head]._name, model[head]._id);
|
|
|
+
|
|
|
+ const [registeredModel] = await registerModels(data);
|
|
|
+
|
|
|
+ model[head] = registeredModel;
|
|
|
+
|
|
|
+ this._loadedRelations.push(fullPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rest.length === 0) return;
|
|
|
+
|
|
|
+ await model[head].loadRelations(rest.join("."));
|
|
|
+ }
|
|
|
+
|
|
|
+ public async loadRelations(
|
|
|
+ relations?: string | string[],
|
|
|
+ force = false
|
|
|
+ ): Promise<void> {
|
|
|
+ if (relations)
|
|
|
+ relations = Array.isArray(relations) ? relations : [relations];
|
|
|
+
|
|
|
+ await Promise.all(
|
|
|
+ (relations ?? []).map(async path => {
|
|
|
+ await this._loadRelation(this, path, force);
|
|
|
+ })
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ public async unregisterRelations(): Promise<void> {
|
|
|
+ const { unregisterModels } = useModelStore();
|
|
|
+ const relationIds = await Promise.all(
|
|
|
+ this._loadedRelations.map(async path => {
|
|
|
+ const relation = await this._getRelation(path);
|
|
|
+ return relation._id;
|
|
|
+ })
|
|
|
+ );
|
|
|
await unregisterModels(relationIds);
|
|
|
}
|
|
|
|
|
@@ -113,7 +160,7 @@ export default class Model {
|
|
|
|
|
|
Object.assign(this, data);
|
|
|
|
|
|
- await this.loadRelations();
|
|
|
+ await this.loadRelations(this._loadedRelations, true);
|
|
|
|
|
|
await this.refreshPermissions();
|
|
|
}
|