All Projects → bashleigh → typeorm-polymorphic

bashleigh / typeorm-polymorphic

Licence: MIT license
Typeorm polymorphic relationship management

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to typeorm-polymorphic

nest-convoy
[WIP] An opinionated framework for building distributed domain driven systems using microservices architecture
Stars: ✭ 20 (-85.4%)
Mutual labels:  typeorm
ADMMutate
Classic code from 1999+ I am fairly sure this is the first public polymorphic shellcode ever (best IMHO and others http://ids.cs.columbia.edu/sites/default/files/ccs07poly.pdf :) If I ever port this to 64 or implement a few other suggestions (sorry I lost ppc code version contributed) it will be orders of magnitude more difficult to spot, so I h…
Stars: ✭ 69 (-49.64%)
Mutual labels:  polymorphic
babel-example
Example how to use TypeORM with JavaScript + Babel.
Stars: ✭ 51 (-62.77%)
Mutual labels:  typeorm
Wern-Fullstack-Template
React, Next.js, MaterialUI, Styled-Components, TypeGraphQL, URQL, ApolloServer (express), TypeORM, PostgreSQL, Node.js, TypeScript
Stars: ✭ 173 (+26.28%)
Mutual labels:  typeorm
node-ts-api-base
REST API boilerplate made with Express + NodeJS
Stars: ✭ 87 (-36.5%)
Mutual labels:  typeorm
IssueTracker-40
Github IssueTracker Clone Project
Stars: ✭ 18 (-86.86%)
Mutual labels:  typeorm
sf-midway-admin
🚀 基于MidwayJs + TypeScript + TypeORM + Redis + MySql + Vue2 + Element-UI编写的一款简单高效的前后端分离的权限管理系统
Stars: ✭ 93 (-32.12%)
Mutual labels:  typeorm
typeorm-factory
Typeorm factory that makes testing easier
Stars: ✭ 28 (-79.56%)
Mutual labels:  typeorm
ecoleta-api
♻ API of an application to help people find collection points for recycling, made with express, typescript and typeorm - Next Level Week
Stars: ✭ 16 (-88.32%)
Mutual labels:  typeorm
nestjs-starter
🚀 Nest framework starter
Stars: ✭ 30 (-78.1%)
Mutual labels:  typeorm
server
Core server in the Alkemio platform, offering a GraphQL api for interacting with the logical domain model.
Stars: ✭ 20 (-85.4%)
Mutual labels:  typeorm
prime-nestjs
A production-ready NestJS boilerplate using Typescript, Postgres, TypeORM, and Docker.
Stars: ✭ 140 (+2.19%)
Mutual labels:  typeorm
sf-nest-admin
🚀 基于NestJs + TypeScript + TypeORM + Redis + MySql + Vue2 + Element-UI编写的一款简单高效的前后端分离的权限管理系统
Stars: ✭ 125 (-8.76%)
Mutual labels:  typeorm
nest-rest-typeorm-boilerplate
🍱 backend with nest (typescript), typeorm, and authentication
Stars: ✭ 142 (+3.65%)
Mutual labels:  typeorm
typeorm-factories
Create factories for your TypeORM entities. Useful for NestJS applications
Stars: ✭ 43 (-68.61%)
Mutual labels:  typeorm
uni-pushy-server
upushy 热更新后端。https://upushy.yoouu.cn/
Stars: ✭ 30 (-78.1%)
Mutual labels:  typeorm
microservice-template
📖 Nest.js based microservice repository template
Stars: ✭ 131 (-4.38%)
Mutual labels:  typeorm
typeplate
REST API boilerplate with Typescript, Express.js, Typeorm and Mocha.
Stars: ✭ 268 (+95.62%)
Mutual labels:  typeorm
api-server-nodejs
Nodejs API Server - Express / SQLite / TypeORM | AppSeed
Stars: ✭ 171 (+24.82%)
Mutual labels:  typeorm
angular-react-microfrontend
🚧 React vs Angular ? Why not both ! Micro frontend demo using Angular and React alongs with a NodeJS API
Stars: ✭ 17 (-87.59%)
Mutual labels:  typeorm

typeorm-polymorphic

Coverage Status

An extension package for polymorphic relationship management, declaration and repository queries for typeorm

Experiemental package

Install

$ yarn add typeorm-polymorphic

You'll also require typeorm and reflect-metadata if you haven't already installed these

This is a concept I've put together for decorated polymorphic values with typeorm. I've taken a lot of inspiration from laravel's eloquent.

This has worked for my use case however it might not for others. This is an example of how I've used it.

Extend the PolymorphicRepository

@EntityRepository(AdvertEntity)
export class AdvertRepository extends AbstractPolymorphicRepository<
  AdvertEntity
> {}

The below decorators will only work when using the above abstract repository AbstractPolymorphicRepository

Setup the entities

This is an example of one child, 2 parent types

Parents

@Entity('users')
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @PolymorphicChildren(() => AdvertEntity, {
    eager: false,
  })
  adverts: AdvertEntity[];
}
Entity('merchants')
export class MerchantEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @PolymorphicChildren(() => AdvertEntity, {
    eager: false,
  })
  adverts: AdvertEntity[];
}

Children

@Entity('adverts') 
export class AdvertEntity implements PolymorphicChildInterface {
  @PolymorphicParent(() => [UserEntity, MerchantEntity])
  owner: UserEntity | MerchantEntity;

  @Column()
  entityId: number;

  @Column()
  entityType: string;
}

Resulting values

This will result in the adverts table having values

adverts table
==========================
id | entityId | entityType
==========================
 1 | 1        | 'UserEntity'
 2 | 1        | 'MerchantEntity'
 3 | 2        | 'UserEntity'

Decorators

Both PolymorphicChildren and PolymophicParent are treated same. Currently some of the default values are different but eventually these method should be synonyms of one another. They have different names because it helped me describe the relationship directions which could be explained as 'parent' 'child' in different ways.

Ambiguous direction

Both PolymorphicParent and PolymorphicChildren accepts either an array of types or a singular type

@PolymorphicChildren(() => [ChildEntity, AnotherChildEntity])
@PolymorphicParent(() => [ParentEntity, AnotherParentEntity])

@PolymorphicChildren(() => ChildEntity)
@PolymorphicParent(() => ParentEntity)

Options

key what's it for? default
eager load relationships by default true
cascade save/delete parent/children on save/delete true
deleteBeforeUpdate delete relation/relations before update false
hasMany should return as array? true for child. false for parent

hasMany should really be updated so both parent and child declaration are the same. I've done to hopefully avoid confusion from the names!

Repository Methods

The majority of these methods overwrite the typeorm's Repository class methods to ensure polymorph relationships are handled before/after the parent's method.

save

Saves the given entity and it's parent or children

extends typeorm's Repository.save method

Child
const repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository

const advert = new AdvertEntity();
advert.owner = user;

await repository.save(advert);
Parent
const repository = connection.getRepository(MerchantRepository); // That extends AbstractPolymorphicRepository

const advert = new AdvertEntity();

const merchant = new MerchantEntity();
merchant.adverts = [advert];

await repository.save(merchant);

find

extends typeorm's Repository.find method

const repository = connection.getRepository(MerchantRepository); // That extends AbstractPolymorphicRepository

const results = await repository.find();

// results[0].adverts === AdvertEntity[]

findOne

extends typeorm's Repository.findOne method

create

This method creates the parent or child relations for you so you don't have to manally supply an array of classes.

extends typeorm's Repository.create method

Child
const repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository

const results = await repository.create({
  owner: new UserEntity(), // or MerchantEntity()
});
Parent
const repository = connection.getRepository(UserRepository); // That extends AbstractPolymorphicRepository

const results = await repository.create({
  adverts: [
    {
      name: 'test',
    },
    {
      name: 'test',
    },
  ],
});

/**
 * {
 *   adverts: [
 *     AdvertEntity{
 *       name: 'test',
 *     },
 *     AdvertEntity{
 *       name: 'test',
 *     },
 *   ],
 * }
*/

hydrateMany

Hydreate one entity and get their relations to parent/child

const repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository

const adverts = await repository.find();
// eager to parent (user|merchant) is set to false
adverts[0].owner; // undefined

await repository.hydrateMany(adverts);

adverts[0].owner; // UserEntity | MerchantEntity

hydrateOne

Hydreate one entity and get their relations to parent/child

const repository = connection.getRepository(AdvertRepository); // That extends AbstractPolymorphicRepository

const advert = await repository.findOne(1);
// eager to parent (user|merchant) is set to false
advert.owner; // undefined

await repository.hydrateOne(advert);

advert.owner; // UserEntity | MerchantEntity

Class-transformer

We recommend if you're working with polymorphic relationships that you use class-transformers's Transform decorator to distinguish the different types on the frontend when returning your entities from a http call

@Entity('adverts') 
export class AdvertEntity implements PolymorphicChildInterface {
  @PolymorphicParent(() => [UserEntity, MerchantEntity])
  @Transform(
    (value: UserEntity | MerchantEntity) => ({
      ...value,
      type: value.constructor.name,
    }),
    {
      toPlainOnly: true,
    },
  )
  owner: UserEntity | MerchantEntity;

  @Column()
  entityId: number;

  @Column()
  entityType: string;
}

The owner property object's type property will now either be string value of UserEntity or MerchantEntity

Possible relations

Singular parent, different children

This is an example of having the need of different types of children for a singular parent type

class RestaurantEntity {
  @PolymorphicChildren(() => [WaiterEntity, ChefEntity])
  staff: (WaiterEntity | ChefEntity)[];
}

class WaiterEntity implements PolymorphicChildInterface {
  @Column()
  entityId: string;

  @Column()
  entityType: string;

  @PolymorphicParent(() => RestaurantEntity)
  restaurant: RestaurantEntity;
}

class ChefEntity implements PolymorphicChildInterface {
  @Column()
  entityId: string;

  @Column()
  entityType: string;

  @PolymorphicParent(() => RestaurantEntity)
  restaurant: RestaurantEntity;
}

Singular child, different parent

This is an example of having the need of a singular child shared between different types of parents

class AdvertEntity implements PolymorphicChildInterface {
  @PolymorphicParent(() => [UserEntity, MerchantEntity])
  owner: UserEntity | MerchantEntity;
}

class MerchantEntity {
  @PolymorphicChildren(() => AdvertEntity)
  adverts: AdvertEntity[];
}

class UserEntity {
  @PolymorphicChildren(() => AdvertEntity)
  adverts: AdvertEntity[];
}

Notes

I think Perf might have some suggestions on how to improve things (sorry I have replied been mega busy!)

Nestjs

If you're using nestjs, don't forgot to include your repository into the entities array in forFeature

@Module({
  imports: [
    TypeOrmModule.forFeature([
      AdvertEntity,
      AdvertRepository,
    ]),
  ],
  providers: [AdvertService, CategoryService, TagService, AdvertPolicy],
  exports: [TypeOrmModule, AdvertService],
})
export class AdvertModule {}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].