How to add create an entity inside another entity (NestJS)? - javascript

I have two entities: User and Photo with relationship OneToMany. One user can have many photos.
User:
#Entity('users')
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Exclude()
#Column()
password: string;
#Column({ default: '' })
avatar: string;
#OneToMany(() => Photo, photo => photo.user, { cascade: true })
photos: Photo[];
}
And Photo:
#Entity('photos')
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#Column({default: ""})
description: string;
#ManyToOne(() => User, user => user.photos)
user: User;
}
So, my question is: how can i add new photo to specific user? I tried to do something like this, but it doesn't work as expected.
#Patch(':id/photos')
addPhoto(#Param('id') id: number, #Body() createPhotoDto: CreatePhotoDto){
return this.userService.createImage(id, createPhotoDto);
}
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
const newUser = await this.usersRepository.preload({
id: +id,
photos: [...user.photos, createPhotoDto]
});
return this.usersRepository.save(newUser);
}

What I got from the CreatePhotoDto that you save the image and bind it with the user,
one of the easiest way to do it, First declare you imageRepository in your service then :
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
return await this.imageRepository.save({url:createPhotoDto.url,user:user});
// if you want to return the user with the image added you make another find with the relation exp:
// return await this.usersRepository.findOne(id,{ relations: ['photos'],});
}

Related

Using entity inside entity with relationships and populating database in Nest JS / TypeORM

Let's say we are constructing our very first entities for a new web application.
There is an entity for users:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
username: string;
#Column()
password: string;
#OneToOne(() => Author)
author: Author;
Users can be Authors after registration. So there's a one-to-one relationship between users and authors table. Let's say we have another table called books, an author can have multiple books.
export class Author {
#PrimaryGeneratedColumn()
id: number;
#Column()
Name: string;
#OneToMany(() => Book, (Book) => Book.Author)
Book: Book[];
#OneToOne(() => User)
#JoinColumn()
User: User;
Here is a sample entity for Books repository:
export class Book {
#PrimaryGeneratedColumn()
id: number;
#Column()
Name: string;
#Column()
ISIN: string;
#OneToOne(() => Author)
#JoinColumn()
Author: Author;
The question is, when we migrated these entities and built our very first/clean database, how can we insert the data via calling API?
A sample service for API Post method:
#Injectable()
export class BookService {
constructor(
#InjectRepository(Book)
private BookRepository: Repository<Book>,
) {}
async create(createBookDto: CreateBookDto) {
await this.BookRepository.insert(createBookDto);
}
}
And the Controller:
#Controller('books')
export class BookController {
constructor(private readonly BookService: BookService) {}
#Post('/Book')
create(#Body() createBookDto: CreateBookDto) {
return this.BookService.create(createBookDto);
}
}
The problem here is that when I want to create a new book by POSTing data to the API route, it needs the Author to be defined. So how can I post existing user-books-author data into the database via this service?
The best option I think of is to create a new instance of the classes, get the data from request #Body and assign it to the objects of the class then save it to the database.
But I think it's not a good solution, as it's very preferred to use repositories instead of object-class type.
I think you do a mistake in your entity definition. There is OneToMany relation between Author to Book.
Modify your book entity like this
export class Book {
#PrimaryGeneratedColumn()
id: number;
#Column()
Name: string;
#Column()
ISIN: string;
#ManyToOne(() => Author, author=> author.book, { nullable: true })
#JoinColumn()
Author: Author;
{ nullable: true } this will allow you to save your book data without having an author.
Modify your service to save the book information
public async create(createBookDTO: CreateBookDTO) {
try {
if (
createBookDTO.hasOwnProperty('author') &&
createTaskDTO.author
) {
const author = await this.connection.manager.findOne(Author, {
where: {
id: createBookDTO.author,
},
});
if (author) {
createBookDTO.author = author;
} else {
throw new NotAcceptableException('Author not found');
}
}
return await this.bookRepo.save(createBookDTO);
} catch (err) {
throw new HttpException(err, err.status || HttpStatus.BAD_REQUEST);
}
}

ER_PARSE_ERROR in Nest js

Hello i have problem with relations in NestJS.
I was trying to get user with relation named club like that:
async findOne(id: number): Promise<User> {
return this.userRepository.findOne({
where: { id: id },
relations: ['club'],
});
}
And that throws this error:
ER_PARSE_ERROR: You have an error in your SQL syntax;
check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE `User`.`id` = 1) `distinct Alias` ORDER BY `User_id` ASC LIMIT 1' at line 1
I think my entites look correct, can it be problem with MariaDB?
There is my User entity:
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
#Unique('email_unique', ['email'])
email: string;
#Column()
#Unique('username_unique', ['username'])
username: string;
#Column()
password: string;
#Column({ type: 'boolean' })
active = false;
#Column()
activationToken: string;
#OneToOne(() => Club, (club) => club.user)
club: Club;
}
And Club entity:
#Entity()
export class Club {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => League)
league: League;
#OneToOne(() => User, (user) => user.club)
user: User;
#Column()
name: string;
#Column()
location: string;
#OneToMany(() => Player, (player) => player.club)
players: Player[];
}
You are missing a JoinColumn annotation - it could be the reason for the erroneous generated sql. Try defining the JoinColumn on the owning side, e.g.
export class User {
// ...
#OneToOne(() => Club, (club) => club.user)
#JoinColumn()
club: Club;
}

TypeOrm find relations with object data mapper

I have users who can have multiple emails associated. I want to accomplish two things given below using data object mapper. How can I do that?
I want to find all users in a given array.
const emails = ['xyz#opc.com', 'abc#hdc.com'];
const users = await findUsersWithEmails(emails);
Find a user with an email.
const email = 'xyz#opc.com';
const user = await findUserWithEmail(email);
Entities
#Entity('email')
export class Email extends BaseEntity {
#Column({ nullable: false, unique: true })
email: string;
#Column({ default: false })
verified: boolean;
#ManyToOne(type => User, user => user.emails)
user: Promise<User>;
}
#Entity('users')
export class User extends BaseEntity {
#Column({ nullable: false })
name: string;
#OneToMany(type => Phone, phone => phone.user)
emails: Promise<Email[]>;
}

How to add multiple relations one at a time in TypeORM

So i've been struggling with this for some time now, i have this many to many realtionship in my codebase. Between a User and Odds. I want the User to be able to add an Odds
User Entity:
#Entity()
#Unique(['username'])
export class User extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
username: string;
#Unique(['email'])
#Column()
email: string;
#Column()
password: string;
#Column({ nullable: true })
customerId: string;
#ManyToMany(type => Odds, odds => odds.user)
#JoinTable()
odds: Odds[];
#Column()
salt: string;
async validatePassword(password: string): Promise<boolean> {
const hash = await bcrypt.hash(password, this.salt);
return hash === this.password;
}
}
Odds Entity:
#Entity()
export class Odds extends BaseEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
hometeam: string;
#Column()
awayteam: string;
#Column()
hometeamLogo: string;
#Column()
awayteamLogo: string;
#Column()
bet: string;
#Column({ type: 'real' })
value: string;
#Column()
stake: number;
#Column({ type: 'real' })
bookieOdds: string;
#ManyToMany(type => User, user => user.odds)
user: User[];
}
When i try to add a relation like this
async addOddsToUser(user: User, oddsId: number): Promise<void> {
const id = user.id;
const userDB = await this.userRepository.findOne({ id });
const odds = await this.oddsRepository.findOne({ id: oddsId });
userDB.odds = [odds];
userDB.save();
}
In the associative table it does add the relation the first time, but if i add one more it overrides the first relation, i also tried userDB.odds.push(odds); which doesnt work.
Any help would be appreciated!
The Problem is that your addOddsToUser function overrides the userDB.odds array with a new single array item each time. Thus existing relations will be deleted as mentioned in the FAQ:
When you save the object it will check if there are any categories in the database bind to the question - and it will detach all of them. Why? Because relation equal to [] or any items inside it will be considered like something was removed from it, there is no other way to check if an object was removed from entity or not.
Therefore, saving an object like this will bring you problems - it will remove all previously set categories.

alias was not found. Maybe you forget to join it

In my nectjs project I'm using TypeORM and I have 2 entities user and post,
and I'm tying to make a relation between them
user.entity.ts
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 50, unique: true })
name: string;
#OneToMany(type => Post, post => post.user)
posts: Post[];
}
post.entity.ts
#Entity()
export class Post {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 30 })
title: string;
#ManyToOne(type => User, user => user.posts)
user: User;
}
So I want to join these tables and get post by it's title for specific user
const PostObject = await createQueryBuilder("post")
.leftJoinAndSelect(
"post.user",
"user",
"post.title = :title",
{ title: "title1" }
)
.where("user.id = :id", { id: id })
.getOne();
but when I run the project and execute this function I get this error:
Error: "post" alias was not found. Maybe you forget to join it?
You have to inject the Repository first:
constructor(
#InjectRepository(Post) private readonly postRepository: Repository<Post>
) {
}
Then you can create the QueryBuilder for the Post entity by passing in the alias:
this.postRepository.createQueryBuilder('post')
// ^^^^^^^^^^^^^^^^
.leftJoinAndSelect(
'post.user',
'user',
'post.title = :title',
{ title: 'title1' },
)
.where('user.id = :id', { id })
.getOne();
Can also use getRepository like below.
const PostObject = await getRepository("post").createQueryBuilder("post")
.leftJoinAndSelect(
"post.user",
"user",
"post.title = :title",
{ title: "title1" }
)
.where("user.id = :id", { id: id })
.getOne();

Categories