TriPSs/nestjs-query

Need an example in the documentation of how to work with inherited entities

Opened this issue ยท 2 comments

๐Ÿ“š Documentation

We need an example in the documentation of how to work with inherited entities.

There are two Entities in the same table.
How do I make FilterableUnpagedRelation with a common DTO?

registerEnumType(ContactType, {
  name: 'ContactType',
});

@Entity('person_contact')
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export class ContactEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @ManyToOne(() => PersonEntity, (person) => person.id)
  @JoinColumn({ name: 'owner_id' })
  ownerId: PersonEntity;

  @Column({ type: 'text', nullable: true })
  description: string;

  type: ContactType;
  email: string;
  phone: string;
}
@ChildEntity(ContactType.email)
export class EmailContactEntity extends ContactEntity {
  @Column({
    nullable: false,
    type: 'varchar',
    length: 255,
  })
  @IsEmail()
  @Index({ unique: true })
  email: string;
}
@ChildEntity(ContactType.phone)
export class PhoneContactEntity extends ContactEntity {
  @Column({
    nullable: false,
    type: 'varchar',
    length: 25,
    comment: '(DC2Type:phone_number)',
  })
  @IsPhoneNumber('RU')
  @Index({ unique: true })
  phone: string;
}

I need to transform PhoneContactEntity EmailContactEntity to
ContactDTO where value is either a phone number or an email.

@ObjectType('contact')
export class ContactDTO {
  @Field(() => ID)
  id: string;

  @FilterableField(() => ContactType, { nullable: false })
  type: ContactType;

  @FilterableField()
  value: string;

  @Field({ nullable: true })
  description: string;
}

I'm trying to do it through assemblers, but how use two assemblers for one DTO
Or choose a way to store data in different tables?

Could you not just create resolver etc for ContactDTO and add a @ResolveField for value that returns one of the two?

We do something similiar for the Creator type, which is just a UserEntity in the database:

import { Parent, ResolveField, Resolver } from '@nestjs/graphql'
import { UserType } from '@constants/user'
import { UserEntity } from '@database/main/user.entity'

import { Creator, CreatorType } from './creator.type'

@Resolver(() => Creator)
export class CreatorResolver {

  @ResolveField()
  public name(@Parent() creator: UserEntity): string {
    if (this.isSpecialUser(creator)) {
      return creator.fullName
    }

    return creator.firstName
  }

  @ResolveField()
  public type(@Parent() creator: UserEntity): string {
    if (this.isSpecialUser(creator)) {
      switch (creator.type) {
        case UserType.API:
          return CreatorType.API

        case UserType.System:
          return CreatorType.System

        case UserType.Integration:
          return CreatorType.Integration
      }
    }

    return CreatorType.User
  }

  private isSpecialUser(creator: UserEntity) {
    return [UserType.API, UserType.Integration, UserType.System].includes(creator.type as UserType)
  }

}

This is not exactly the pattern. When we use ChildEntity, we create our own Entity and DTO for each Child.
Resolver should convert ChildEntity to ContactDTO based on the type. ContactEntity is abstract.
I wrote such a Resolver, but automatic filtering in the scheme does not work on it

User {
name: String!
contacts: [ContactDTO]
}

I realized that this is not an easy solution, so for now I keep all types of contacts in one column and additionally verify before saving