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;
}
Related
I'm tackling a NestJS course and it's just a bit outdated and at the moment the requirement is to have a "Create" method on the entity class, I can see that the instructor is able to have it functioning but I keep getting an error.
#Entity()
#Unique('task_index_uq', ['title'])
export class Task extends Repository<Task> {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
title: string;
#Column()
description: string;
#Column()
status: TaskStatus;
async createTask(createTaskDto: CreateTaskDto): Promise<Task> {
const { title, description } = createTaskDto;
const task = this.create({
title,
description,
status: TaskStatus.OPEN,
});
await this.save(task);
return task;
}
}
What could be the problem?
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);
}
}
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'],});
}
I'm trying to use automapper-ts to map from dto to entity and vice versa.
The creation of the mappings seems fine to me but when I try to map I always get an object without values.
The config:
automapper
.createMap(CityDTO, City)
.forMember('createdBy', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore())
.forMember('createdAt', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore())
.forMember('updatedBy', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore())
.forMember('updatedAt', (opts: AutoMapperJs.IMemberConfigurationOptions) => opts.ignore());
// These are properties present in the entity but not in the DTO
And this is the mapping method:
const fromCityToCityDto = (source: CityDTO, destination?: City): City => {
if (!destination) {
destination = new City();
}
// This produces 'source name' and 'source population'
Object.keys(source).forEach((key) => console.log('source', key));
// This never runs, it seems it doesn't have any key
Object.keys(destination).forEach((key) => console.log('destination', key));
destination = automapper.map(CityDTO, City, source);
return destination;
};
As ORM I'm using typeorm. This is the entity class:
export class EntityBase extends BaseEntity {
#PrimaryGeneratedColumn({ name: 'Id' })
id: number;
#Column({ name: 'CreatedBy', length: 50 })
createdBy: string;
#CreateDateColumn({ name: 'CreatedAt' })
createdAt: Date;
#Column({ name: 'UpdatedBy', nullable: true, length: 50 })
updatedBy: string;
#CreateDateColumn({ name: 'UpdatedAt', nullable: true })
updatedAt: Date;
}
#Entity('Cities')
export class City extends EntityBase {
#Column({ name: 'Name', length: 50 })
name: string;
#Column({ name: 'Population' })
population: number;
}
EDIT: This is the result of console.log:
source id
source name
source population
source createdBy
source createdAt
source updatedBy
source updatedAt
Looks like the destination doesn't have any property.
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.