Skip to content

fix: Take into account parent's primaryColumn config when fetching fr… #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/__tests__/entities/admin.entity.ts
Original file line number Diff line number Diff line change
@@ -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[];
}
5 changes: 3 additions & 2 deletions src/__tests__/entities/advert.entity.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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;
Expand Down
36 changes: 31 additions & 5 deletions src/__tests__/polymorphic.repository.spec.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -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,
});
Expand Down Expand Up @@ -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<AdvertEntity> =
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,
}),
);

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,
Expand Down
25 changes: 19 additions & 6 deletions src/polymorphic.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,25 @@ export abstract class AbstractPolymorphicRepository<
options: PolymorphicMetadataInterface,
): Promise<PolymorphicChildInterface[] | PolymorphicChildInterface | never> {
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)],
},
}
: {
Expand Down Expand Up @@ -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<E>;
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;
});
}
Expand Down