1、src/app/actions
此目录的作用是放置与数据库交互的逻辑,一般这里的action会被服务端渲染的页面调用比如:src/app/listings/[listingId]/page.tsx
2、src/app/types 用于定义额外的类型,比如:
import { User ,Listing ,Reservation} from "@prisma/client";
export type SafeListing = Omit<Listing,"createdAt"> & {
createdAt:string
}
export type SafeReservation = Omit<
Reservation,
"createdAt"|"startDate"|"endDate"|"listing"
>
& {
createdAt:string;
startDate:string;
endDate:string;
listing:SafeListing;
}
export type SafeUser = Omit<
User,
"createdAt"|"updatedAt"|"emailVerified"
>
& {
createdAt:string;
updatedAt:string;
emailVerified:string | null;
}
3、使用prisma进行update操作
import { Listing } from '@prisma/client';
import { NextRequest, NextResponse } from "next/server";
import prisma from "@/app/libs/prismadb";
import getCurrentUser from "@/app/actions/getCurrentUser";
export async function POST( request:Request)
{
const currentUser = await getCurrentUser();
if(!currentUser){
return NextResponse.error();
}
const body = await request.json();
const {
listingId,
startDate,
endDate,
totalPrice
} = body;
if(!listingId || !startDate ||!endDate||!totalPrice){
return NextResponse.error();
}
const listingAndReservations = await prisma.listing.update({
where:{
id:listingId
},
data:{
reservations:{
create:{
userId:currentUser.id,
startDate,
endDate,
totalPrice
}
}
}
})
return NextResponse.json(listingAndReservations);
}
这是一个更新操作,它会先找到消ID等于listingId
的清单,然后在reservations
中创建一个新的预约,这个预约包含了userId
,startDate
,endDate
以及totalPrice
。
首先,await prisma.listing.update
表示它正等待(因为是异步)Prisma的update方法来更新一个listing(清单)。
然后,where: { id:listingId }
是查找条件,即找到数据库数据中id属性等于listingId
的清单。
在data
部分,表示要在找到的清单上进行的更新操作。这里的操作是在reservations
中创建一个新的预约。
reservations: {create: {userId:currentUser.id, startDate, endDate, totalPrice}}
中:
create
表示创建新的预约。userId:currentUser.id
表示这个预约的用户ID就是当前用户的ID。startDate
和endDate
分别表示预约的开始和结束日期。totalPrice
表示预约的总价格。
最后,这个函数会返回一个Promise,等待更新操作完成,然后返回更新后的清单对象。
4、数据库设计
通过上述update操作,引起了我对数据库设计的兴趣,prisma如何组织复杂的表间关系呢?看一下下面的实际例子:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
email String? @unique
emailVerified DateTime?
image String?
hashedPassword String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
favoriteIds String[] @db.ObjectId
accounts Account[]
listings Listing[]
reservations Reservation[]
}
model Account{
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
type String
provider String
providerAccountId String
refresh_token String? @db.String
access_token String? @db.String
expires_at Int?
token_type String?
scope String?
id_token String? @db.String
session_state String?
user User @relation(fields: [userId], references: [id],onDelete:Cascade)
@@unique([provider,providerAccountId])
}
model Listing{
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
description String
imageSrc String
createdAt DateTime @default(now())
category String
roomCount Int
bathroomCount Int
guestCount Int
locationValue String
userId String @db.ObjectId
price Int
user User @relation(fields: [userId], references: [id],onDelete:Cascade)
reservations Reservation[]
}
model Reservation {
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
listingId String @db.ObjectId
startDate DateTime
endDate DateTime
totalPrice Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
}
让我们关注表的部分:
在这个 Prisma 模型中,我们有 User
, Account
, Listing
, 和 Reservation
四个模型。这些模型直接的关系如下:
User
和Account
:这是一对多的关系。每个用户可以有多个账号,但每个账号只能属于一个用户。这个关系通过User
中的accounts Account[]
和Account
中的user User @relation(fields: [userId], references: [id],onDelete:Cascade)
建立。User
和Listing
:这也是一对多的关系。每个用户可以发布多个清单,但每个清单只能由一个用户发布。这个关系通过User
中的listings Listing[]
和Listing
中的user User @relation(fields: [userId], references: [id],onDelete:Cascade)
建立。User
和Reservation
:这是一对多的关系。每个用户可以创建多个预约,但每个预约只能归属于一个用户。关系通过User
中的reservations Reservation[]
和Reservation
中的user User @relation(fields: [userId], references: [id], onDelete: Cascade)
建立。Listing
和Reservation
:这是一对多的关系。每个清单可以拥有多个预约,但每个预约只能关联一个清单。这个关系通过Listing
中的reservations Reservation[]
和Reservation
中的listing Listing @relation(fields: [listingId], references: [id], onDelete: Cascade)
建立。
确定表关系的方式通常要根据实际情况来分析。一般来说,你需要根据你的业务需求以及数据的特性来确定。例如,如果一个用户可以拥有多个账号,那么这就是一对多的关系。如果一个预约只能关联一个清单,那么这就是一对一的关系。总的来说,确定哪种关系需要用在哪个表之间,需要对你的业务逻辑有深入的理解。
5、prisma 实现左联查询
//“我想查看我的所有房源的预定信息”
if(authorId){
query.listing = {userId:authorId};
}
const reservations = await prisma.reservation.findMany({
where:query,
include:{
listing:true
},
orderBy:{
createdAt:"desc"
}
})
在这个例子中,如果我希望查询的是“预约(reservation)”,但是条件来自于关联表“房源”( listing) 下的字段 userId时,我们将会如上表示。
在这个查询中,listingId=authorId
表示我们正在寻找那些 listing 的所有者(即 userId
)是 authorId
的预约记录。换句话说,我们正在查找指定用户(authorId
)作为房东的所有预约。
这确实是一种深层次的查询,因为我们不只是查看 reservation
表的记录,还要进一步查看与这些记录相关联的 listing
表的记录。通过这种方式,我们能够将两张表中的数据关联起来,提供全面的信息,如房东的所有预约记录等。