MyProfile.(schema.prisma, sales, purchase, favs)

김종민·2022년 8월 8일
0

apple-market

목록 보기
23/37


들어가기
나의 Profile칸을 완선
0. schema.prisma
1. 판매내역, 구매내역, 관심목록
2. EditProfile
3. 나에 대한 Review

1. schema.prisma

sales, purchases, fav를 따로따로 구현해도 되고,
enum으로 만들어서(kind)로 분류해도 된다.
아래에서는 두가지 모두 구현해 놓음.

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["referentialIntegrity"]
}

datasource db {
  provider             = "mysql"
  url                  = env("DATABASE_URL")
  referentialIntegrity = "prisma"
}

model User {
  id              Int         @id @default(autoincrement())
  createdAt       DateTime    @default(now())
  updatedAt       DateTime    @updatedAt
  phone           String?     @unique
  email           String?     @unique
  name            String
  avatar          String?
  tokens          Token[]
  products        Product[]
  fav             Fav[]           ///Enum으로 나누지 않고 따로만듬
  sales           Sale[]           ///Enum으로 나누지 않고 따로만듬
  purchases       Purchase[]        ///Enum으로 나누지 않고 따로만듬
  posts           Post[]
  answers         Answer[]
  wonderings      Wondering[]
  writtenReviews  Review[]    @relation("writtenRiviews")
   ///내가 쓴 Review와 나에대해 쓰여진 Review를 구분지음
   
  receivedReviews Review[]    @relation("receivedRiviews")
   ///내가 쓴 Review와 나에대해 쓰여진 Review를 구분지음
   
  records         Record[]  ///sales, purchases, favs를 enum으로 만들때,
                            ///records로 사용!!!
}

model Token {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  payload   String   @unique
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int
}

model Product {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId      Int
  image       String
  name        String
  price       Int
  description String   @db.MediumText
  favs        Fav[]        ///Enum으로 나누지 않고 따로만듬
  sales       Sale[]        ///Enum으로 나누지 않고 따로만듬
  purchases   Purchase[]      ///Enum으로 나누지 않고 따로만듬
  record      Record[]         ///sales, purchases, favs를 enum으로 만들때,
                            ///records로 사용!!!
}

model Post {
  id        Int         @id @default(autoincrement())
  createdAt DateTime    @default(now())
  updatedAt DateTime    @updatedAt
  user      User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int
  question  String      @db.MediumText
  answers   Answer[]
  wondering Wondering[]
  latitude  Float?
  longitude Float?
}

model Answer {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int
  answer    String   @db.MediumText
  post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  postId    Int
}

model Wondering {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int
  post      Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  postId    Int
}

model Review {
  id           Int      @id @default(autoincrement())
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
  review       String   @db.MediumText
  createdBy    User     @relation(name: "writtenRiviews", fields: [createdById], references: [id], onDelete: Cascade)
  ///내가 쓴 리뷰, name을 넣어서 나에대해 쓰여진 리뷰와 구분지어줌.
  
  createdFor   User     @relation(name: "receivedRiviews", fields: [createdForId], references: [id], onDelete: Cascade)
  ///나에게 쓰여진 리뷰, name을 넣어서 내가 쓴 리뷰와 구분지어줌.
  
  createdById  Int
  createdForId Int
  score        Int      @default(1) ///별점 넣기!!
}

model Fav {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  product   Product  @relation(fields: [productId], references: [id], onDelete: Cascade)
  userId    Int
  productId Int
}

model Sale {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  product   Product  @relation(fields: [productId], references: [id], onDelete: Cascade)
  userId    Int
  productId Int
}

model Purchase {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  product   Product  @relation(fields: [productId], references: [id], onDelete: Cascade)
  userId    Int
  productId Int
}

model Record {   //favs, purchases, sales로 따로 만들지 않고 하나로 만들때,
                 ///맨 밑에 kind의 type을 Kind로 해 놓음.
                 /// 그리고 enum Kind를 만들어줌.
                 /// -->/api/users/me/record?kind=sale (req.query로 확인가능)
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  product   Product  @relation(fields: [productId], references: [id], onDelete: Cascade)
  userId    Int
  productId Int
  kind      Kind
}


enum Kind {
  Purchase
  Sale
  Fav
}

2. pagea/api/users/fav.ts

import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  const { user } = req.session
  const favs = await client.fav.findMany({
  ///favs에 fav DB에서 where에서 나의 id로 찾은 fav를
  ///favs에 담아서 return해줌, product를 포함하고, 
  ///각각의 product에 favs counting을 넣어줌.
  ///sales, purchase도 똑같음.
  
    where: {
      userId: user?.id,
    },
    include: {
      product: {
        include: {
          _count: {
            select: {
              favs: true,
            },
          },
        },
      },
    },
  })
  res.json({
    ok: true,
    favs,
  })
}
export default withApiSession(
  withHandler({
    methods: ['GET'],
    handler,
  })
)

3. pages/api/users/sales.ts

import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  const { user } = req.session

  //   client.record.findMany({
  //     where:{
  //         userId:user?.id,
  //         kind:"Sale" //enum은 지정한 것만 들어갈수 있다.
  //     }
  //   })

  const sales = await client.sale.findMany({
    where: {
      userId: user?.id,
    },
    include: {
      product: {
        include: {
          _count: {
            select: {
              favs: true,
            },
          },
        },
      },
    },
  })
  res.json({
    ok: true,
    sales,
  })
}
export default withApiSession(
  withHandler({
    methods: ['GET'],
    handler,
  })
)

4. pagea/api/users/purchases.ts

import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  const { user } = req.session
  const purchases = await client.purchase.findMany({
    where: {
      userId: user?.id,
    },
    include: {
      product: {
        include: {
          _count: {
            select: {
              favs: true,
            },
          },
        },
      },
    },
  })
  res.json({
    ok: true,
    purchases,
  })
}
export default withApiSession(
  withHandler({
    methods: ['GET'],
    handler,
  })
)

5.pages/profile/index.tsx

profile index화면에서는 Link만 걸어주면 끝

          <Link href="/profile/sold"> ///////////////////Link걸어줌.
            <a className="flex flex-col items-center">
              <div className="w-14 h-15 text-white bg-orange-500 rounded-full p-3 flex items-center justify-center">
                <svg
                  className="w-6 h-6"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth="2"
                    d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
                  ></path>
                </svg>
              </div>
              <span className="text-sm font-medium text-gray-700 mt-2">
                판매내역
              </span>
            </a>
          </Link>

6. components/product-list.tsx

sales, favs, purchases 모두 같은 구조이기 때문에,
공통적으로 data를 부르는 부분을 component로 만듬

import { ProductWithCount } from 'pages'
import useSWR from 'swr'
import Item from './item'

interface ProductListProps {
  kind: 'favs' | 'sales' | 'purchases'
}
///ProductListProps를 kind로 type을 만듬.

interface Record {
  id: number
  product: ProductWithCount
}
///count가 포함된 Product type인 ProductWithCount(product 페이지에서 만들어놓음)
///를 만듬.
///Record에 id를 널어줌.

interface ProductListResponse {
  [key: string]: Record[]
}
///[ket:string] 는 걍 모든 type다 가능, 위에서 만든 Record배열
///상품을 뿌려주어야 하니깐.

export default function ProductList({ kind }: ProductListProps) {
  const { data } = useSWR<ProductListResponse>(`/api/users/me/${kind}`)
  ///kind를 입력받는데, 위에서 만든 ProductLostProps임.
  
  return data ? (
  ///return은 받는 data가 있으면, data?.[kind]?.map으로 뿌려진것을 return함.
  ///반드시 맨 위와 밑에 fragment로 감싸줌. <></>
    <>
      {data?.[kind]?.map((record) => (
        <Item
          key={record.id}
          id={record.product.id}
          title={record.product.name}
          price={record.product.price}
          comments={7}
          hearts={record.product._count.favs}
        ></Item>
      ))}
    </>
  ) : null
}

7. pages/profile/loved.tsx

위에서 만든 product-list덕분에 profile/index.tsx에서
관심목록을 클릭했을때, 구현되는 page.
엄청시리 간단함.

import ProductList from '@components/product-list'
import type { NextPage } from 'next'
import Layout from '../../components/layout'

const Loved: NextPage = () => {
  return (
    <Layout title="관심목록" canGoBack>
      <div className="flex flex-col space-y-5 py-10 ">
        <ProductList kind="favs" /> ///kind에 favs만 넣어주면 됨.
                                    ///sales, purchases만 넣어주면,
                                    ///판매내역, 구매내역 구현됨.
      </div>
    </Layout>
  )
}

export default Loved
profile
코딩하는초딩쌤

0개의 댓글