gpt4 book ai didi

TypeORM 多态关系

转载 作者:行者123 更新时间:2023-12-03 09:37:52 24 4
gpt4 key购买 nike

我正在使用 TypeORM 将 Laravel 应用程序迁移到 Node 应用程序。有没有人能够实现类似于 Laravel's Polymorphic Relations 的东西?在 TypeOrm 中?

我试图重现的示例架构:

export class Notification {
id: string;
attachable_id: number;
attachable_type: string;
}

我希望能够拥有一个可以是任何类型的notification.attachable 关系。然后,理想情况下,我可以急切地向用户加载他们最近的 x 条通知,并在每个通知上附加附件。

最佳答案

编辑:

所以我进行了重构/重写,并将其全部放入仓库 https://github.com/bashleigh/typeorm-polymorphic

所以,我一直在考虑尝试为此实现一些东西。我有 2 天的时间来匆忙实现一些东西,所以我做了这个粗鲁的东西。

import {
FindManyOptions,
DeepPartial,
ObjectID,
FindConditions,
UpdateResult,
Repository,
SaveOptions,
} from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';

export interface PolymorphicInterface {
entityId: string;
entityType: string;
}

export type PolyMorphicType<K> = PolymorphicInterface & DeepPartial<K>;

export const POLYMORPHIC_RELATIONSHIP = 'POLYMORPHIC_RELATIONSHIP';

export interface PolymorphicOptions {
type: Function;
parent: Function;
property: string | Symbol;
}

export const PolyMorphic = (type: Function): PropertyDecorator => (
target: Object,
propertyKey: string | Symbol,
): void =>
Reflect.defineMetadata(
`${POLYMORPHIC_RELATIONSHIP}::${propertyKey}`,
{
type,
parent: target.constructor.name,
property: propertyKey,
},
target,
);

export class PolymorphicRepository<T extends DeepPartial<T>> extends Repository<T> {
private getMetadata(): Array<PolymorphicOptions> {
let keys = Reflect.getMetadataKeys((this.metadata.target as Function)['prototype']);

if (!Array.isArray(keys)) {
return [];
}

keys = keys.filter((key: string) => {
const parts = key.split('::');
return parts[0] === POLYMORPHIC_RELATIONSHIP;
});

if (!keys) {
return [];
}

return keys.map(
(key: string): PolymorphicOptions =>
Reflect.getMetadata(key, (this.metadata.target as Function)['prototype']),
);
}

async find(findOptions?: FindConditions<T> | FindManyOptions<T>): Promise<T[]> {
const polymorphicMetadata = this.getMetadata();

if (Object.keys(polymorphicMetadata).length === 0) {
return super.find(findOptions);
}

const entities = await super.find(findOptions);

return this.hydratePolymorphicEntities(entities);
}

public async hydratePolymorphicEntities(entities: Array<T>): Promise<Array<T>> {
const metadata = this.getMetadata();

metadata.forEach(
async (data: PolymorphicOptions): Promise<void> => {
await Promise.all(
entities.map(
async (entity: T): Promise<void> => {
const repository = this.manager.getRepository(data.type);
const property = data.property;
const parent = data.parent;

if (!repository) {
throw new Error(
`Repository not found for type [${
data.type
}] using property [${property}] on parent entity [${parent}]`,
);
}

const morphValues = await repository.find({
where: {
//@ts-ignore
entityId: entity.id, // TODO add type AbstractEntity
entityType: this.metadata.targetName,
},
});

//@ts-ignore
entity[property] = morphValues;
},
),
);
},
);

return entities;
}

public async update(
criteria:
| string
| string[]
| number
| number[]
| Date
| Date[]
| ObjectID
| ObjectID[]
| FindConditions<T>,
partialEntity: QueryDeepPartialEntity<T>,
): Promise<UpdateResult> {
const polymorphicMetadata = this.getMetadata();
if (Object.keys(polymorphicMetadata).length === 0) {
return super.update(criteria, partialEntity);
}

const result = super.update(criteria, partialEntity);

// TODO update morphs
throw new Error("CBA I'm very tired");

return result;
}

public async save<E extends DeepPartial<T>>(
entity: E | Array<E>,
options?: SaveOptions & { reload: false },
): Promise<E & T | Array<E & T>> {
const polymorphicMetadata = this.getMetadata();

if (Object.keys(polymorphicMetadata).length === 0) {
return Array.isArray(entity) ? super.save(entity, options) : super.save(entity);
}

const result = Array.isArray(entity)
? await super.save(entity, options)
: await super.save(entity);

Array.isArray(result)
? await Promise.all(result.map((res: T) => this.saveMorphs(res)))
: await this.saveMorphs(result);

return result;
}

private async saveMorphs(entity: T): Promise<void> {
const metadata = this.getMetadata();

await Promise.all(
metadata.map(
async (data: PolymorphicOptions): Promise<void> => {
const repository: Repository<PolymorphicInterface> = this.manager.getRepository(
data.type,
);
const property = data.property;
const parent = data.parent;
const value: Partial<PolymorphicInterface> | Array<Partial<PolymorphicInterface>> =
//@ts-ignore
entity[property];

if (typeof value === 'undefined' || value === undefined) {
return new Promise(resolve => resolve());
}

if (!repository) {
throw new Error(
`Repository not found for type [${
data.type
}] using property [${property}] on parent entity [${parent}]`,
);
}

let result: Array<any> | any;

if (Array.isArray(value)) {
//@ts-ignore
result = await Promise.all(
value.map(val => {
// @ts-ignore
val.entityId = entity.id;
val.entityType = this.metadata.targetName;
return repository.save(
value instanceof data.type ? value : repository.create(value),
);
}),
);
} else {
// @ts-ignore
value.entityId = entity.id; // TODO resolve AbstractEntity for T
value.entityType = this.metadata.targetName;

result = await repository.save(
value instanceof data.type ? value : repository.create(value),
);
}

// @ts-ignore
entity[property] = result;
},
),
);
}
}


这很粗糙,但这就是我为解决这个问题而实现的。基本上,我已经实现了我自己的方法,通过创建我自己的存储库来处理元数据中定义的实体的保存。

然后你可以像这样使用它

@Entity()
export class TestEntity {
@PolyMorphic(SomeOtherEntity)
property: SomeOtherEntity[];
}

The typings are really bad but that's only because I've had 1 days to implement this feature and I did it on the plane

关于TypeORM 多态关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52995280/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com