들어가기
order를 만드는 방법
매우 어려우므로 집중해야함.
특히, option부분이 매우 난해함.
option(name, choice, extra)부분
import { Field, InputType, Int, ObjectType } from '@nestjs/graphql';
import { MutationOutput } from 'src/common/dtos/output.dto';
import { OrderItemOption } from '../entities/order-item.entity';
@InputType()
class CreateOrderItemInput {
@Field(() => Int)
dishId: number;
@Field(() => OrderItemOption, { nullable: true })
options?: OrderItemOption[];
}
@InputType()
export class CreateOrderInput {
@Field(() => Int)
restaurantId: number;
///식당 Id가 들어가야함.
@Field(() => [CreateOrderItemInput])
items: CreateOrderItemInput[];
///주문할 음식(items)은 CreateOrderItemInput배열로 받음!!
}
@ObjectType()
export class CreateOrderOutput extends MutationOutput {}
!!.CreateOrderInput Dto는 restaurantId와 items:CreateOrderItemInput[] 두개로 한다.
!!.CreateOrderItemInput은 dishId와 dishId의 dish에 따른 options:OrderItemOption[]으로
구성된다. OrderItemOption은 order-item.entity안에 만들어 놓은것임.
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import { AuthUser } from 'src/auth/auth-user.decorator';
import { Role } from 'src/auth/role.decorator';
import { User } from 'src/users/entities/user.entity';
import { CreateOrderInput, CreateOrderOutput } from './dtos/create-order.dto';
import { Order } from './entities/order.entity';
import { OrdersService } from './orders.service';
@Resolver(() => Order)
export class OrdersResolver {
constructor(private readonly ordersService: OrdersService) {}
@Mutation(() => CreateOrderOutput)
@Role(['Client']) ///Order는 Client만 만들수 있게 한다.
async createOrder(
@AuthUser() customer: User, ///loggedInUser는 customer:User로 받는다.
@Args('input') createOrderInput: CreateOrderInput,
): Promise<CreateOrderOutput> {
return this.ordersService.createOrder(customer, createOrderInput);
}
///customer와 createOrderInput를 넣는다.
}
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Dish } from 'src/restaurant/entities/dish.entity';
import { Restaurant } from 'src/restaurant/entities/restaurant.entity';
import { User } from 'src/users/entities/user.entity';
import { Repository } from 'typeorm';
import { CreateOrderInput, CreateOrderOutput } from './dtos/create-order.dto';
import { OrderItem } from './entities/order-item.entity';
import { Order } from './entities/order.entity';
@Injectable()
export class OrdersService {
constructor(
@InjectRepository(Order)
private readonly orders: Repository<Order>,
@InjectRepository(Restaurant)
private readonly restaurants: Repository<Restaurant>,
@InjectRepository(OrderItem)
private readonly orderItems: Repository<OrderItem>,
@InjectRepository(Dish)
private readonly dishes: Repository<Dish>,
) {}
///createOrder를 위해서 Order, Restaurant, OrderItem, Dish
///Entity를 모두 불러준다.
async createOrder(
customer: User,
{ restaurantId, items }: CreateOrderInput,
): Promise<CreateOrderOutput> {
///restaurantId와 items를 input한다.
///items는 CreateOrderItemInput[]
///CreateOrderItemInput은
///@InputType()
///class CreateOrderItemInput {
/// @Field(() => Int)
/// dishId: number;
/// @Field(() => OrderItemOption, { nullable: true })
/// options?: OrderItemOption[]; OrderItemOption[]은 oreder-item entity안에있음
/// name:string, choice?:string, extra:number로 구성됨.
try {
const restaurant = await this.restaurants.findOne(restaurantId);
if (!restaurant) {
return {
ok: false,
error: 'Restaurant not found',
};
}
///먼저, 입력받은 restaurantId로 restaurant찾음. 없음 error
let orderFinalPrice = 0;
///주문 최종가격을 let으로 설정해서 일단 0으로 잡아놓음.
const orderItems: OrderItem[] = [];
///orderItems를 만들어서 빈배열을 default값으로!
///type은 OrderItem[]배열로! OrederItem은 dish, options로 구성된 entity
for (const item of items) {
///맨 위에서 restaurantId와 함께 입력받은 items를 하나씩 구분함.
///items는 (dishId와 options)로 구성됨.
const dish = await this.dishes.findOne(item.dishId);
///dishId로 item의 dish를 찾음.
if (!dish) {
return {
ok: false,
error: 'Dish not found',
};
}
///못찾으면 error를 리턴함.
let dishFinalPrice = dish.price;
///dish가격을 let으로 설정해 놓음.
for (const itemOption of item.options) {
///분리한 각각의 item의 option들을 분리함.(사이즈, 맵기정도, 토핑등등)
///item.option은 OrderItemOption으로 oreder-item entity안에있음
const dishOption = dish.options.find(
(dishOption) => dishOption.name === itemOption.name,
);
///dish option에서 Order option이랑 같은 단어를 찾음.
///Size:L 라고Order option에서 입력한 거를 dish option에서
///찾아서 return해줌. 여기서 name은 Size, 맛등을 말함.
///choice는 XL, L, sm, very Spice, Spice등등..
///아래에 dish entity의 DishOption이랑, DishChoice를 참고할 것.
if (dishOption) {
if (dishOption.extra) {
dishFinalPrice = dishFinalPrice + dishOption.extra;
///json이 일치하는 option에 extra라 있으면, dishFinalPrice에 더함.
} else {
const dishOptionChoice = dishOption.choices.find(
(optionChoice) => optionChoice.name === itemOption.choice,
);
///dishOption.choices Entity에서 item.Options.choice와
///name이 같은 것을 찾아서 optionChoice로 return 해줌.
if (dishOptionChoice) {
if (dishOptionChoice.extra) {
dishFinalPrice = dishFinalPrice + dishOptionChoice.extra;
}
///찾은 option에 Extra가 있으면, dishFinalPrice에 더해줌.
}
}
}
}
orderFinalPrice = orderFinalPrice + dishFinalPrice;
///다 더해진 dishFinalPrice를 orderFinalPrice에 더해줌.
const orderItem = await this.orderItems.save(
this.orderItems.create({
dish,
options: item.options,
}),
);
///OrderItem entity에 위에서 나온 dish와 options들을 더해서
///orderItem을 만들고 저장해줌.
orderItems.push(orderItem);
///orederItems에 위에서 만든 orderItem을 push해 줌.
}
await this.orders.save(
this.orders.create({
customer,
restaurant,
total: orderFinalPrice,
items: orderItems,
}),
);
///위에서 만든것들을, orders entity에 create 및 save해줌.
return {
ok: true,
};
} catch {
return {
ok: false,
error: 'Could not create Order.',
};
}
}
}
@InputType('DishChoiceInputType', { isAbstract: true })
@ObjectType()
export class DishChoice {
@Field(() => String)
name: string;
@Field(() => Int, { nullable: true })
extra?: number;
}
@InputType('DishOptionInputType', { isAbstract: true })
@ObjectType()
export class DishOption {
@Field(() => String)
name: string;
@Field(() => [DishChoice], { nullable: true })
choices?: DishChoice[];
@Field(() => Int, { nullable: true })
extra?: number;
}
지금까지 만든 API중에 가장 어려운 부분이니
집중해서 봐야할 부분.