From e6fb28b87e7a315b7fa52074ce104a5a4bc549dd Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 25 Nov 2025 15:49:08 +0330 Subject: [PATCH] category --- .../foods/controllers/category.controller.ts | 12 ++-- src/modules/foods/entities/category.entity.ts | 7 ++- .../foods/providers/category.service.ts | 63 ++++++++++--------- src/seeders/DatabaseSeeder.ts | 25 +++++--- 4 files changed, 58 insertions(+), 49 deletions(-) diff --git a/src/modules/foods/controllers/category.controller.ts b/src/modules/foods/controllers/category.controller.ts index b6c0842..d293470 100644 --- a/src/modules/foods/controllers/category.controller.ts +++ b/src/modules/foods/controllers/category.controller.ts @@ -36,21 +36,17 @@ export class CategoryController { @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Post('admin/categories') - create(@Body() dto: CreateCategoryDto) { - return this.categoryService.create(dto); + create(@Body() dto: CreateCategoryDto, @RestId() restId: string) { + return this.categoryService.create(restId, dto); } @ApiOperation({ summary: 'my restaurant categories' }) @ApiOkResponse({ description: 'List of categories' }) - @ApiQuery({ name: 'restId', required: false, type: String }) - @ApiQuery({ name: 'isActive', required: false, type: Boolean }) @UseGuards(AdminAuthGuard) @ApiBearerAuth() @Get('admin/categories') - findAll(@RestId() restId: string, @Query('isActive') isActive?: string) { - // convert isActive from query string to boolean when provided - const isActiveBool: boolean | undefined = typeof isActive !== 'undefined' ? isActive === 'true' : undefined; - return this.categoryService.findAll({ restId, isActive: isActiveBool }); + findAll(@RestId() restId: string) { + return this.categoryService.findAllByRestaurantId(restId); } @UseGuards(AdminAuthGuard) diff --git a/src/modules/foods/entities/category.entity.ts b/src/modules/foods/entities/category.entity.ts index 6999320..aa1159f 100644 --- a/src/modules/foods/entities/category.entity.ts +++ b/src/modules/foods/entities/category.entity.ts @@ -1,6 +1,7 @@ -import { Entity, Property, Collection, OneToMany } from '@mikro-orm/core'; +import { Entity, Property, Collection, OneToMany, ManyToOne } from '@mikro-orm/core'; import { Food } from './food.entity'; import { BaseEntity } from '../../../common/entities/base.entity'; +import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity'; @Entity({ tableName: 'categories' }) export class Category extends BaseEntity { @@ -13,8 +14,8 @@ export class Category extends BaseEntity { @Property({ default: true }) isActive: boolean = true; - @Property({ nullable: true }) - restId?: string; + @ManyToOne(() => Restaurant) + restaurant!: Restaurant; @Property({ nullable: true }) avatarUrl?: string; diff --git a/src/modules/foods/providers/category.service.ts b/src/modules/foods/providers/category.service.ts index c15cdaa..6ab1ca6 100644 --- a/src/modules/foods/providers/category.service.ts +++ b/src/modules/foods/providers/category.service.ts @@ -3,7 +3,7 @@ import { CreateCategoryDto } from '../dto/create-category.dto'; import { UpdateCategoryDto } from '../dto/update-category.dto'; import { CategoryRepository } from '../repositories/category.repository'; import { EntityManager } from '@mikro-orm/postgresql'; -import { RequiredEntityData, FilterQuery } from '@mikro-orm/core'; +import { RequiredEntityData } from '@mikro-orm/core'; import { Category } from '../entities/category.entity'; import { CategoryMessage, RestMessage } from 'src/common/enums/message.enum'; import { Restaurant } from 'src/modules/restaurants/entities/restaurant.entity'; @@ -15,12 +15,15 @@ export class CategoryService { private readonly em: EntityManager, ) {} - async create(dto: CreateCategoryDto): Promise { + async create(restId: string, dto: CreateCategoryDto): Promise { + const restaurant = await this.em.findOne(Restaurant, { id: restId }); + if (!restaurant) { + throw new NotFoundException(RestMessage.NOT_FOUND); + } const data: RequiredEntityData = { title: dto.title, isActive: dto.isActive ?? true, - //TODO - restId: '01KA35CFFN0F6VA04DF0W6KCE3', + restaurant: restaurant, avatarUrl: dto.avatarUrl, }; @@ -34,37 +37,41 @@ export class CategoryService { if (!restaurant || !restaurant.id) { throw new NotFoundException(RestMessage.NOT_FOUND); } - return this.categoryRepository.find({ restId: restaurant.id.toString() }); + return this.categoryRepository.find({ restaurant: restaurant }); } - async findAll(opts?: { restId?: string; isActive?: boolean }): Promise { - const where: FilterQuery = {}; - if (opts?.restId) where.restId = opts.restId; - if (opts && typeof opts.isActive === 'boolean') where.isActive = opts.isActive; - const cats = await this.categoryRepository.find(where, { populate: ['foods'] }); - - // Return plain objects to avoid circular references during JSON serialization - return cats.map(cat => ({ - id: cat.id, - title: cat.title, - isActive: cat.isActive, - restId: cat.restId, - avatarUrl: cat.avatarUrl, - createdAt: cat.createdAt, - updatedAt: cat.updatedAt, - foods: cat.foods.getItems().map(f => ({ id: f.id, title: f.title })), - })) as unknown as Category[]; + async findAllByRestaurantId(restId: string): Promise { + return this.categoryRepository.find({ restaurant: { id: restId } }); } + // async findAll(opts?: { restId?: string; isActive?: boolean }): Promise { + // const where: FilterQuery = {}; + // if (opts?.restId) where.restId = opts.restId; + // if (opts && typeof opts.isActive === 'boolean') where.isActive = opts.isActive; + // const cats = await this.categoryRepository.find(where, { populate: ['foods'] }); + + // // Return plain objects to avoid circular references during JSON serialization + // return cats.map(cat => ({ + // id: cat.id, + // title: cat.title, + // isActive: cat.isActive, + // restId: cat.restId, + // avatarUrl: cat.avatarUrl, + // createdAt: cat.createdAt, + // updatedAt: cat.updatedAt, + // foods: cat.foods.getItems().map(f => ({ id: f.id, title: f.title })), + // })) as unknown as Category[]; + // } + async findOne(restId: string, id: string): Promise { - const cat = await this.categoryRepository.findOne({ id, restId }, { populate: ['foods'] }); + const cat = await this.categoryRepository.findOne({ id, restaurant: { id: restId } }, { populate: ['foods'] }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); return { id: cat.id, title: cat.title, isActive: cat.isActive, - restId: cat.restId, + restaurant: cat.restaurant, avatarUrl: cat.avatarUrl, createdAt: cat.createdAt, updatedAt: cat.updatedAt, @@ -73,14 +80,14 @@ export class CategoryService { } async findRestaurantCategories(restId: string): Promise { - const cat = await this.categoryRepository.findOne({ restId }, { populate: ['foods'] }); + const cat = await this.categoryRepository.findOne({ restaurant: { id: restId } }, { populate: ['foods'] }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); return { id: cat.id, title: cat.title, isActive: cat.isActive, - restId: cat.restId, + restaurant: cat.restaurant, avatarUrl: cat.avatarUrl, createdAt: cat.createdAt, updatedAt: cat.updatedAt, @@ -88,7 +95,7 @@ export class CategoryService { } async update(restId: string, id: string, dto: UpdateCategoryDto): Promise { - const cat = await this.categoryRepository.findOne({ id, restId }); + const cat = await this.categoryRepository.findOne({ id, restaurant: { id: restId } }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); this.em.assign(cat, dto); await this.em.persistAndFlush(cat); @@ -96,7 +103,7 @@ export class CategoryService { } async remove(restId: string, id: string): Promise { - const cat = await this.categoryRepository.findOne({ id, restId }); + const cat = await this.categoryRepository.findOne({ id, restaurant: { id: restId } }); if (!cat) throw new NotFoundException(CategoryMessage.NOT_FOUND); cat.deletedAt = new Date(); await this.em.persistAndFlush(cat); diff --git a/src/seeders/DatabaseSeeder.ts b/src/seeders/DatabaseSeeder.ts index d245c5a..275a359 100644 --- a/src/seeders/DatabaseSeeder.ts +++ b/src/seeders/DatabaseSeeder.ts @@ -235,23 +235,28 @@ export class DatabaseSeeder extends Seeder { // 6. Create Categories (Farsi) const categories = [ - { title: 'غذای اصلی', restId: zhivan?.id }, - { title: 'پیش غذا', restId: zhivan?.id }, - { title: 'دسر', restId: zhivan?.id }, - { title: 'نوشیدنی', restId: zhivan?.id }, - { title: 'غذای اصلی', restId: boote?.id }, - { title: 'پیش غذا', restId: boote?.id }, - { title: 'دسر', restId: boote?.id }, - { title: 'نوشیدنی', restId: boote?.id }, + { title: 'غذای اصلی', restaurant: zhivan }, + { title: 'پیش غذا', restaurant: zhivan }, + { title: 'دسر', restaurant: zhivan }, + { title: 'نوشیدنی', restaurant: zhivan }, + { title: 'غذای اصلی', restaurant: boote }, + { title: 'پیش غذا', restaurant: boote }, + { title: 'دسر', restaurant: boote }, + { title: 'نوشیدنی', restaurant: boote }, ]; const createdCategories: Category[] = []; for (const catData of categories) { - const existing = await em.findOne(Category, { title: catData.title, restId: catData.restId }); + if (!catData.restaurant) continue; + + const existing = await em.findOne(Category, { + title: catData.title, + restaurant: catData.restaurant, + }); if (!existing) { const category = em.create(Category, { title: catData.title, - restId: catData.restId, + restaurant: catData.restaurant, isActive: true, }); em.persist(category);