diff --git a/src/__tests__/entities/admin.entity.ts b/src/__tests__/entities/admin.entity.ts new file mode 100644 index 0000000..fd8059d --- /dev/null +++ b/src/__tests__/entities/admin.entity.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { PolymorphicChildren } from '../../../dist'; +import { AdvertEntity } from './advert.entity'; + +@Entity('admins') +export class AdminEntity { + @PrimaryGeneratedColumn() + admin_id: number; + + @PolymorphicChildren(() => AdvertEntity, { + eager: false, + primaryColumn: "admin_id" + }) + adverts: AdvertEntity[]; +} diff --git a/src/__tests__/entities/advert.entity.ts b/src/__tests__/entities/advert.entity.ts index a307f86..23dda92 100644 --- a/src/__tests__/entities/advert.entity.ts +++ b/src/__tests__/entities/advert.entity.ts @@ -1,5 +1,6 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; import { PolymorphicParent } from '../../../dist'; +import { AdminEntity } from './admin.entity'; import { MerchantEntity } from './merchant.entity'; import { UserEntity } from './user.entity'; @@ -8,10 +9,10 @@ export class AdvertEntity { @PrimaryGeneratedColumn() id: number; - @PolymorphicParent(() => [UserEntity, MerchantEntity], { + @PolymorphicParent(() => [UserEntity, MerchantEntity, AdminEntity], { eager: true, }) - owner: UserEntity | MerchantEntity; + owner: UserEntity | MerchantEntity | AdminEntity; @Column({ nullable: true }) entityId: number; diff --git a/src/__tests__/polymorphic.repository.spec.ts b/src/__tests__/polymorphic.repository.spec.ts index badec5c..9b20e73 100644 --- a/src/__tests__/polymorphic.repository.spec.ts +++ b/src/__tests__/polymorphic.repository.spec.ts @@ -1,11 +1,12 @@ -import { DataSource, Repository } from 'typeorm'; -import { AdvertEntity } from './entities/advert.entity'; -import { UserEntity } from './entities/user.entity'; import { config } from 'dotenv'; import { resolve } from 'path'; -import { AdvertRepository } from './repository/advert.repository'; +import { DataSource } from 'typeorm'; import { AbstractPolymorphicRepository } from '../'; +import { AdminEntity } from './entities/admin.entity'; +import { AdvertEntity } from './entities/advert.entity'; import { MerchantEntity } from './entities/merchant.entity'; +import { UserEntity } from './entities/user.entity'; +import { AdvertRepository } from './repository/advert.repository'; import { UserRepository } from './repository/user.repository'; describe('AbstractPolymorphicRepository', () => { @@ -22,7 +23,7 @@ describe('AbstractPolymorphicRepository', () => { port: parseInt(process.env.TYPEORM_PORT as string, 10), username: process.env.TYPEORM_USERNAME, password: process.env.TYPEORM_PASSWORD, - entities: [UserEntity, AdvertEntity, MerchantEntity], + entities: [UserEntity, AdvertEntity, MerchantEntity, AdminEntity], synchronize: process.env.TYPEORM_SYNCHRONIZE === 'true', database: process.env.TYPEORM_DATABASE, }); @@ -164,6 +165,31 @@ describe('AbstractPolymorphicRepository', () => { expect(result?.entityType).toBe(UserEntity.name); }); + it('Can find entity with parent even with different primaryColumn', async () => { + const repository: AbstractPolymorphicRepository = AbstractPolymorphicRepository.createRepository( + connection, + AdvertRepository, + ); + const adminRepository = connection.getRepository(AdminEntity); + + const admin = await adminRepository.save(new AdminEntity()); + + const advert = await repository.save( + repository.create({ + owner: admin, + }), + ); + console.log(advert); + + const result = await repository.findOne({ where: { id: advert.id } }); + + expect(result).toBeInstanceOf(AdvertEntity); + expect(result?.owner).toBeInstanceOf(AdminEntity); + const owner = result?.owner as AdminEntity; + expect(owner.admin_id).toBe(result?.entityId); + expect(result?.entityType).toBe(AdminEntity.name); + }); + it('Can find entity without parent', async () => { const repository = AbstractPolymorphicRepository.createRepository( connection, @@ -266,5 +292,8 @@ describe('AbstractPolymorphicRepository', () => { expect(result?.adverts[0].entityId).toBe(user.id); }); }); + + }); + }); diff --git a/src/polymorphic.repository.ts b/src/polymorphic.repository.ts index 98fe616..f6b1d11 100644 --- a/src/polymorphic.repository.ts +++ b/src/polymorphic.repository.ts @@ -181,13 +181,25 @@ export abstract class AbstractPolymorphicRepository< options: PolymorphicMetadataInterface, ): Promise { const repository = this.findRepository(entityType); + // If we are in the child repository, `options` has the child metadata + // but not the parent's while options.type == 'parent'. But to + // perform the query below we need the ID of the parent entity. Meaning + // we need to read the `primaryColum` option from the parent repository. + + // Act as if the parent repository is the current repository + // and extract the metadata from it + // FIXME: Come up with a nicer interface to do this + const metadata: PolymorphicMetadataInterface[] = this.getPolymorphicMetadata.bind(repository)(); + + // Find the metadata associated to the current repository through `target` + const found = metadata.find((m) => m.classType === this.target); return repository[options.hasMany ? 'find' : 'findOne']( options.type === 'parent' ? { where: { // TODO: Not sure about this change (key was just id before) - [PrimaryColumn(options)]: parent[entityIdColumn(options)], + [PrimaryColumn({ primaryColumn: found.primaryColumn, ...options })]: parent[entityIdColumn(options)], }, } : { @@ -266,14 +278,15 @@ export abstract class AbstractPolymorphicRepository< return entity; } - /** - * Add parent's id and type to child's id and type field - */ + // FIXME: Come up with a nicer interface to do this + const repository = this.findRepository(parent.constructor as Function); + const parentMetadata: PolymorphicMetadataInterface[] = this.getPolymorphicMetadata.bind(repository)(); + const found = parentMetadata.find((m) => m.classType === this.target); type EntityKey = keyof DeepPartial; entity[entityIdColumn(options) as EntityKey] = - parent[PrimaryColumn(options)]; + parent[PrimaryColumn({ primaryColumn: found.primaryColumn, ...options })]; entity[entityTypeColumn(options) as EntityKey] = - parent.constructor.name; + parent.constructor.name; return entity; }); }