作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 TypeORM 将 Laravel 应用程序迁移到 Node 应用程序。有没有人能够实现类似于 Laravel's Polymorphic Relations 的东西?在 TypeOrm 中?
我试图重现的示例架构:
export class Notification {
id: string;
attachable_id: number;
attachable_type: string;
}
最佳答案
编辑:
所以我进行了重构/重写,并将其全部放入仓库 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/
我来自 Asp.Net 世界,试图理解 Angular State 的含义。 什么是 Angular 状态?它类似于Asp.Net中的ascx组件吗?是子页面吗?它类似于工作流程状态吗? 我听到很多人
我一直在寻找 3 态拨动开关,但运气不佳。 基本上我需要一个具有以下状态的开关: |开 |不适用 |关 | slider 默认从中间开始,一旦用户向左或向右滑动,就无法回到N/A(未回答)状态。 有人
我是一名优秀的程序员,十分优秀!