up
This commit is contained in:
@@ -3,10 +3,9 @@ import { ApiPropertyOptional } from '@nestjs/swagger';
|
|||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
|
|
||||||
export class CreateCategoryDto {
|
export class CreateCategoryDto {
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@ApiPropertyOptional({ example: 'Beverages' })
|
@ApiPropertyOptional({ example: 'Beverages' })
|
||||||
title?: string;
|
title!: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Entity, Property, ManyToMany, Collection } from '@mikro-orm/core';
|
import { Entity, Property, ManyToMany, Collection } from '@mikro-orm/core';
|
||||||
import { Foods } from './food.entity';
|
import { Food } from './food.entity';
|
||||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'categories' })
|
@Entity({ tableName: 'categories' })
|
||||||
export class Category extends BaseEntity {
|
export class Category extends BaseEntity {
|
||||||
@Property({ nullable: true })
|
@Property()
|
||||||
title?: string;
|
title!: string;
|
||||||
|
|
||||||
@ManyToMany(() => Foods, food => food.categories)
|
@ManyToMany(() => Food, food => food.categories)
|
||||||
foods = new Collection<Foods>(this);
|
foods = new Collection<Food>(this);
|
||||||
|
|
||||||
@Property({ default: true })
|
@Property({ default: true })
|
||||||
isActive: boolean = true;
|
isActive: boolean = true;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Category } from './category.entity';
|
|||||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'foods' })
|
@Entity({ tableName: 'foods' })
|
||||||
export class Foods extends BaseEntity {
|
export class Food extends BaseEntity {
|
||||||
@ManyToMany(() => Category)
|
@ManyToMany(() => Category)
|
||||||
categories = new Collection<Category>(this);
|
categories = new Collection<Category>(this);
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ import { CategorysController } from './controllers/categorys.controller';
|
|||||||
import { CategoryService } from './providers/category.service';
|
import { CategoryService } from './providers/category.service';
|
||||||
import { FoodRepository } from './repositories/food.repository';
|
import { FoodRepository } from './repositories/food.repository';
|
||||||
import { CategoryRepository } from './repositories/category.repository';
|
import { CategoryRepository } from './repositories/category.repository';
|
||||||
|
import { MikroOrmModule } from '@mikro-orm/nestjs';
|
||||||
|
import { Category } from './entities/category.entity';
|
||||||
|
import { Food } from './entities/food.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [MikroOrmModule.forFeature([Food, Category])],
|
||||||
controllers: [FoodController, CategorysController],
|
controllers: [FoodController, CategorysController],
|
||||||
providers: [FoodService, CategoryService, FoodRepository, CategoryRepository],
|
providers: [FoodService, CategoryService, FoodRepository, CategoryRepository],
|
||||||
exports: [FoodRepository, CategoryRepository],
|
exports: [FoodRepository, CategoryRepository],
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ export class CategoryService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async create(dto: CreateCategoryDto): Promise<Category> {
|
async create(dto: CreateCategoryDto): Promise<Category> {
|
||||||
const data = {
|
const data: RequiredEntityData<Category> = {
|
||||||
title: dto.title,
|
title: dto.title,
|
||||||
isActive: dto.isActive ?? true,
|
isActive: dto.isActive ?? true,
|
||||||
restId: dto.restId,
|
restId: dto.restId,
|
||||||
avatarUrl: dto.avatarUrl,
|
avatarUrl: dto.avatarUrl,
|
||||||
} as RequiredEntityData<Category>;
|
};
|
||||||
|
|
||||||
const category = this.categoryRepository.create(data);
|
const category = this.categoryRepository.create(data);
|
||||||
await this.em.persistAndFlush(category);
|
await this.em.persistAndFlush(category);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FoodRepository } from '../repositories/food.repository';
|
|||||||
import { CategoryRepository } from '../repositories/category.repository';
|
import { CategoryRepository } from '../repositories/category.repository';
|
||||||
import { EntityManager } from '@mikro-orm/postgresql';
|
import { EntityManager } from '@mikro-orm/postgresql';
|
||||||
import { RequiredEntityData } from '@mikro-orm/core';
|
import { RequiredEntityData } from '@mikro-orm/core';
|
||||||
import { Foods } from '../entities/food.entity';
|
import { Food } from '../entities/food.entity';
|
||||||
import { FoodMessage } from 'src/common/enums/message.enum';
|
import { FoodMessage } from 'src/common/enums/message.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -16,11 +16,11 @@ export class FoodService {
|
|||||||
private readonly em: EntityManager,
|
private readonly em: EntityManager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async create(createFoodDto: CreateFoodDto): Promise<Foods> {
|
async create(createFoodDto: CreateFoodDto): Promise<Food> {
|
||||||
const { categoryIds, ...rest } = createFoodDto;
|
const { categoryIds, ...rest } = createFoodDto;
|
||||||
|
|
||||||
// prepare data with defaults for required fields so repository.create typing is satisfied
|
// prepare data with defaults for required fields so repository.create typing is satisfied
|
||||||
const data: RequiredEntityData<Foods> = {
|
const data: RequiredEntityData<Food> = {
|
||||||
// boolean/day flags
|
// boolean/day flags
|
||||||
sat: rest.sat ?? false,
|
sat: rest.sat ?? false,
|
||||||
sun: rest.sun ?? false,
|
sun: rest.sun ?? false,
|
||||||
@@ -45,7 +45,7 @@ export class FoodService {
|
|||||||
points: rest.points ?? 0,
|
points: rest.points ?? 0,
|
||||||
prepareTime: rest.prepareTime ?? 0,
|
prepareTime: rest.prepareTime ?? 0,
|
||||||
images: rest.images ?? undefined,
|
images: rest.images ?? undefined,
|
||||||
} as RequiredEntityData<Foods>;
|
} as RequiredEntityData<Food>;
|
||||||
|
|
||||||
const food = this.foodRepository.create(data);
|
const food = this.foodRepository.create(data);
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ export class FoodService {
|
|||||||
return this.foodRepository.findOne({ id }, { populate: ['categories'] });
|
return this.foodRepository.findOne({ id }, { populate: ['categories'] });
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: string, dto: Partial<CreateFoodDto>): Promise<Foods> {
|
async update(id: string, dto: Partial<CreateFoodDto>): Promise<Food> {
|
||||||
const food = await this.foodRepository.findOne({ id });
|
const food = await this.foodRepository.findOne({ id });
|
||||||
|
|
||||||
if (!food) {
|
if (!food) {
|
||||||
@@ -77,7 +77,7 @@ export class FoodService {
|
|||||||
const { categoryIds, ...rest } = dto;
|
const { categoryIds, ...rest } = dto;
|
||||||
|
|
||||||
// assign scalar fields
|
// assign scalar fields
|
||||||
this.em.assign(food, rest as Partial<Foods>);
|
this.em.assign(food, rest as Partial<Food>);
|
||||||
|
|
||||||
// attach new categories if provided (adds, does not attempt to preserve/remove existing ones)
|
// attach new categories if provided (adds, does not attempt to preserve/remove existing ones)
|
||||||
if (Array.isArray(categoryIds) && categoryIds.length) {
|
if (Array.isArray(categoryIds) && categoryIds.length) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
|
||||||
import { FilterQuery } from '@mikro-orm/core';
|
import { FilterQuery } from '@mikro-orm/core';
|
||||||
import { Foods } from '../entities/food.entity';
|
import { Food } from '../entities/food.entity';
|
||||||
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
|
||||||
|
|
||||||
type FindFoodsOpts = {
|
type FindFoodsOpts = {
|
||||||
@@ -15,20 +15,20 @@ type FindFoodsOpts = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FoodRepository extends EntityRepository<Foods> {
|
export class FoodRepository extends EntityRepository<Food> {
|
||||||
constructor(readonly em: EntityManager) {
|
constructor(readonly em: EntityManager) {
|
||||||
super(em, Foods);
|
super(em, Food);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Find foods with pagination and optional filters.
|
* Find foods with pagination and optional filters.
|
||||||
* Supports: search (title/content), categoryId, isActive, ordering.
|
* Supports: search (title/content), categoryId, isActive, ordering.
|
||||||
*/
|
*/
|
||||||
async findAllPaginated(opts: FindFoodsOpts = {}): Promise<PaginatedResult<Foods>> {
|
async findAllPaginated(opts: FindFoodsOpts = {}): Promise<PaginatedResult<Food>> {
|
||||||
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', categoryId, isActive } = opts;
|
const { page = 1, limit = 10, search, orderBy = 'createdAt', order = 'desc', categoryId, isActive } = opts;
|
||||||
|
|
||||||
const offset = (page - 1) * limit;
|
const offset = (page - 1) * limit;
|
||||||
|
|
||||||
const where: FilterQuery<Foods> = {};
|
const where: FilterQuery<Food> = {};
|
||||||
|
|
||||||
if (typeof isActive === 'boolean') {
|
if (typeof isActive === 'boolean') {
|
||||||
where.isActive = isActive;
|
where.isActive = isActive;
|
||||||
@@ -41,7 +41,7 @@ export class FoodRepository extends EntityRepository<Foods> {
|
|||||||
|
|
||||||
if (categoryId) {
|
if (categoryId) {
|
||||||
// filter by related categories (typed via FilterQuery)
|
// filter by related categories (typed via FilterQuery)
|
||||||
Object.assign(where, { categories: { id: categoryId } } as unknown as FilterQuery<Foods>);
|
Object.assign(where, { categories: { id: categoryId } } as unknown as FilterQuery<Food>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [data, total] = await this.findAndCount(where, {
|
const [data, total] = await this.findAndCount(where, {
|
||||||
|
|||||||
Reference in New Issue
Block a user