TriPSs / nestjs-query

Easy CRUD for GraphQL.
https://tripss.github.io/nestjs-query/
MIT License
152 stars 43 forks source link

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

Open KCH0000 opened 3 months ago

KCH0000 commented 3 months ago

📚 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 EmailContactEntityto 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?

TriPSs commented 3 months ago

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)
  }

}
KCH0000 commented 3 months ago

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