Designing a many-to-many entity referencing the same table with TypeORM - javascript

Lets say I want to map some sort of relationship between users as a many-to-many table:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany(() => User)
#JoinTable({ name: 'relationships' })
relationships: User[];
}
How should I go about defining the Relationships entity (supposing I'll want to add custom fields to it down the line?)
#Entity('relationships')
export class Relationships {
#PrimaryGeneratedColumn()
id: number;
#Column()
field: number;
// how do I map the user fields back to the `User` table
user1: User;
user2: User;
}

Following this documentation: (I added the example with courses)
In the entity relationships:
import { Relationship } from "./relationship"
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number
#Column()
name: string
#ManyToMany(() => Relationship, (relationship) => relationship.user)
#JoinTable({
name: 'relationships',
joinColumn: { name: 'userID', referencedColumnName: 'id'},
})
users: this[]
}
import { User } from "./user"
#Entity('relationships')
export class Relationship { // I kept the class in singular
#PrimaryGeneratedColumn()
id: number;
#Column()
public userID!: number
#Column()
field: number;
#ManyToOne(() => User, (user) => user.relationship)
public user!: User
}
I would try something like this?

Related

How to use Typescript and Interfaces when passing props down through React components?

I'm passing down a pretty large array of objects through several React components. I was wondering, what is the syntax for writing the types of all the properties in each object (the objects are nested several times)?
I currently have interfaces like below. These are two components, MainContent, which passes props down into Chart:
MainContent component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function MainContent({ comparatorData, queryData }: params) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}
and Chart component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
// comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function Chart({ queryData }: params): JSX.Element {
...
You can see how redundant it is to be naming these giant, several-times-nested interfaces before every component that uses this array of objects. Is this normal for Typescript? Is there a better way to do something like this? Or does all this data need to be typed upon being passed down through every component?
What forces you to define these identical interfaces explictly for each component?
On the contrary, factorizing them would be the normal choice: that way, they are defined in a single place (single source of truth), and by importing them, you explictly say that you re-use the exact same types.
// Chart.tsx
export interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
export interface DataTypes {
jobId: string;
jobTitle: string;
// etc.
}
export default function Chart({
queryData
}: {
queryData: QueryTypes[];
}) {}
// Main.tsx
import Chart, { QueryTypes } from ".Chart";
import Summary, { ComparatorTypes } from "./Summary"; // Same for ComparatorTypes
export default function MainContent({
comparatorData,
queryData
}: {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}

TypeOrm - Type-GraphQL : entity / resolver: How to create a rating-system

I'm developing an app similar to Google Maps.
The objective is to have users rate places of interest (POI) such as museums and restaurants. Each user can give at most 1 rating to a POI.
Example: user 1 => rates 5 stars => Sushy Palace (restaurant)
This is a simplified database schema:
My (simplified) entities:
entities/user.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { ObjectType, Field } from "type-graphql";
import { Comment } from "./comment";
import { Rate } from "./rate";
#ObjectType()
#Entity()
export class User {
#Field()
#PrimaryGeneratedColumn()
id: number;
#Field()
#Column({ unique: true })
email: string;
#Field()
#Column({ unique: true })
username: string;
#Field({ nullable: true })
#Column({ nullable: true })
profilePicture: string;
#OneToMany(() => Comment, (comment) => comment.user)
public comments!: Comment[];
#OneToMany(() => Rate, (rate) => rate.user)
public rates!: Rate[];
}
entities/pointOfInterest.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToMany
} from "typeorm";
import { ObjectType, Field } from "type-graphql";
import { Comment } from "./comment";
import { Rate } from "./rate";
#ObjectType()
#Entity()
export class PointOfInterest {
#Field()
#PrimaryGeneratedColumn()
id: number;
#Field()
#Column()
name: string;
#OneToMany(() => Comment, (comment) => comment.pointOfInterest)
public comments!: Comment[];
#OneToMany(() => Rate, (rate) => rate.pointOfInterest)
public rates!: Rate[];
}
entities/rate.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm";
import { ObjectType, Field } from "type-graphql";
import { PointOfInterest } from "./pointOfInterest";
import { User } from "./rate";
export enum rateNumbers {
ONE = 1,
TWO = 2,
THREE = 3,
FOUR = 4,
FIVE = 5,
}
#ObjectType()
#Entity()
export class Rate {
#Field()
#PrimaryGeneratedColumn()
id: number;
#Field()
#Column({
type: "enum",
enum: rateNumbers,
default: 4,
})
rate: rateNumbers;
#ManyToOne(() => User, (user) => user.rates)
public user!: User;
#OneToMany(() => PointOfInterest, (pointOfInterest) => pointOfInterest.rates)
public pointOfInterest!: PointOfInterest;
}
Our (simplified) rate resolver create Mutation:
resolvers/rateResolver.ts
import { Arg, Field, InputType, Mutation, Query, Resolver } from "type-graphql";
import { Rate } from "../entities/rate";
import dataSource from "../utils";
import { ApolloError } from "apollo-server";
import { rateNumbers } from "../entities/rate";
#InputType()
class RateType {
#Field()
rate: rateNumbers;
}
#Resolver(Rate)
export class RateResolver {
#Mutation(() => Rate)
async ratePOI(#Arg("data") data: RateType): Promise<Rate | ApolloError> {
const newRate = new Rate();
newRate.rate = data.rate;
newRate.creationDate = new Date();
try {
const rateFormDB = await dataSource.manager.save(Rate, newRate);
console.log(rateFormDB);
return rateFormDB;
} catch (err) {
throw new ApolloError(err.message);
}
}
}
Thanks for your help!

How to use composition in Typegraphql

Im using typeorm and typegraphql to build an API and I would like to abstract out properties of an entity into separate files and then import them to clean up the file:
Example of current
#Entity()
#ObjectType()
export class Person extends BaseEntity {
#Field()
#Column()
name: string;
#Field()
#Column()
surname: string;
#Field()
#Column()
age: number;
#Field()
#Column()
email: string;
}
I would like to do something like this:
class Name {
#Field()
#Column()
name: string;
#Field()
#Column()
surname: string;
}
#Entity()
#ObjectType()
export class Person extends BaseEntity {
#Field()
#Column()
age: number;
#Field()
#Column()
email: string;
// then import the class here
...Name
}
Is there any way to do this without creating separate entities and tables?
Ended up using mixins to solve this because embedded entities doesn't work with both typeorm and typegraphql
export const WithName = <SubClass extends Constructor>(subClass?: SubClass) => {
#ObjectType({ isAbstract: true })
#InputType({ isAbstract: true })
class Mixin extends getFallbackClass(subClass) {
constructor(...args: any[]) {
super(...args);
}
#Field()
#Column()
first: string;
#Field()
#Column()
second: string;
}
return Mixin;
};
then using it like:
class Human extends WithName(class {}) {
}
This is only possible client side by using fragments https://www.apollographql.com/docs/react/data/fragments/
Perhaps you can use something like type inheritance perhaps by using this library https://github.com/nicolasdao/graphql-s2s
Check out this link for reference.

How to select with conditions in relation in TypeORM?

I want to find row in Friendship, where friendship.friends have users with ids equals to given. How can i do it via Repository and QueryBuilder?
Entities:
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { UserToFriendship } from "./userToFriendship.entity";
#Entity()
export class Friendship {
#PrimaryGeneratedColumn()
id: number
#OneToMany(() => UserToFriendship, u => u.friendship, {
eager: true
})
friends: UserToFriendship[]
#Column({ default: false })
accepted: boolean
}
import { User } from "src/users/entities/user.entity";
import { Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { Friendship } from "./friendship.entity";
#Entity()
export class UserToFriendship {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => User, user => user.friendships)
user: User;
#ManyToOne(() => Friendship, friendship => friendship.friends)
friendship: Friendship;
}

Property 'does not exist on type 'DishdetailComponent'

I'm using angular 9 and i got an error.
ERROR in src/app/dishdetail/dishdetail.component.ts:81:9 - error TS2339: Property 'comment' does not exist on type 'DishdetailComponent'.
I want to show a real-time preview of the comment on the page. Here is my code.
dishdetail.component.ts
import { Component, OnInit, ViewChild } from '#angular/core';
import { Params, ActivatedRoute } from '#angular/router';
import { Location } from '#angular/common';
import { Dish } from '../shared/dish';
import { DishService } from '../services/dish.service';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Review } from '../shared/review';
import { Comment } from '../shared/comment';
#Component({
selector: 'app-dishdetail',
templateUrl: './dishdetail.component.html',
styleUrls: ['./dishdetail.component.scss']
})
export class DishdetailComponent implements OnInit {
reviewForm: FormGroup;
review: Review;
#ViewChild('rvwform') reviewFormDirective;
dish: Dish;
formErrorss = {
'author': '',
'comment': ''
};
validationMessagess = {
'author': {
'required': 'Name is required.',
'minlength': 'Author name must be at least 2 characters long.'
},
'comment': {
'required': 'Comment is required.',
'minlength': 'Comment must be at least 5 characters long.'
}
}
constructor(private dishService: DishService, private location: Location, private route: ActivatedRoute, private rf: FormBuilder) {
this.createReview();
}
createReview(){
this.reviewForm = this.rf.group({
'author': ['', [Validators.required, Validators.minLength(2)] ],
'comment': ['', [Validators.required, Validators.minLength(5)] ]
});
this.reviewForm.valueChanges
.subscribe(data => this.onValueChanged(data));
this.onValueChanged();
}
onValueChanged(data?: any) {
if (!this.reviewForm) { return; }
const form = this.reviewForm;
for (const field in this.formErrorss) {
if (this.formErrorss.hasOwnProperty(field)) {
// clear previous error message (if any)
this.formErrorss[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessagess[field];
for (const key in control.errors) {
if (control.errors.hasOwnProperty(key)) {
this.formErrorss[field] += messages[key] + ' ';
}
}
}
}
}
}
onSubmit(reviewForm) {
this.review = this.reviewForm.value;
this.comment.date = new Date().toISOString();
console.log(this.review);
this.dish.comments.push(this.comment)
this.reviewForm.reset({
author: '',
rating: 5,
comment: ''
});
this.reviewFormDirective.resetForm();
}
ngOnInit(): void {
let id = this.route.snapshot.params['id'];
this.dishService.getDish(id)
.subscribe(dish => this.dish = dish);
}
goBack(): void {
this.location.back();
}
}
Here is my code for Review.ts
Review.ts
export class Review{
author: string;
rating: number;
comment: string;
}
enter image description here
Upon submitting the valid comment, the comment should join the regular comments on the page. But im face Property 'comment' does not exist on type 'DishdetailComponent'. Please guide me. Also please check i'm updated the question.
dish.ts
import { Comment } from './Comment';
export class Dish {
id: string;
name: string;
image: string;
category: string;
featured: boolean;
label: string;
price: string;
description: string;
comments: Comment[];
}
This is dishdetails.ts
dishdetails.ts
export class Dishdetails{
id: string;
name: string;
image: string;
category: string;
featured: boolean;
label: string;
price: string;
description: string;
rating: number;
comment: string;
author: string;
date: string;
}
This is comment.ts
comment.ts
export class Comment {
rating: number;
comment: string;
author: string;
date: string;
}
Please check below image for error in my command line.
You are using "this.comment.date" inside OnSubmit(), but "comment" is not defined.
First declare comment.
Or it looks like you want to do this (as date is not used):
this.review = this.reviewForm.value;
//this.comment.date = new Date().toISOString(); //Comment this
console.log(this.review);
this.dish.comments.push(this.review) //changed from this.comment
Additionally, If you're looking to add date in comment, then I suggest to change -
class Review{
author: string;
rating: number;
comment: string;
}
TO
class Review{
author: string;
rating: number;
comment: string,
date: string
}

Categories

Resources