type
Post
status
Published
date
Feb 8, 2026
slug
nextjs-shopping-platform-014
summary
tags
Next.js
category
编程学习
icon
password
😀

1. Section Intro

Now we are going to add the rating and review functionality to the project. 现在我们将为项目添加评分和评论功能。
This includes creaing the Prisma model for reviews as well as the relationship with both the product ans user models and tables. We'll also have some Zod schemas for validation. 这包括创建评论的 Prisma 模型,以及与产品和用户模型及表的关系。我们还将使用一些 Zod 模式进行验证。
We'll create a review list component, which will utlimately show all reviews for that product. 我们将创建一个评论列表组件,最终将显示该产品的所有评论。
We will also have a review form that will open in a dialog. 我们还将有一个在对话框中打开的评论表单。
We will create an action to both create and update the review if the logged in user already reviewed the product. 我们将创建一个 action 来创建和更新评论(如果已登录用户已经评论过该产品)。
Then we'll get the reviews to list on the page in the review list component. 然后我们将获取评论,在评论列表组件中显示在页面上。
We also want to pre-fill the form if there is an existing review and rating. 如果已有评论和评分,我们还希望预先填充表单。

2. Review Prisma Model, Zod & Type 评论 Prisma 模型、Zod 和类型

Now that we are going to be adding reviews and ratings to our products, we need to create a Prisma model for reviews.
既然我们要为产品添加评论和评分功能,我们需要为评论创建一个 Prisma 模型。
Open the prisma/schema.prisma file and add the following code:
打开 prisma/schema.prisma 文件并添加以下代码:
Here is a rundown of the fields:
以下是各字段的说明:
  • id: This is the primary key for the review.
    • id:这是评论的主键。
  • userId: This is the foreign key that references the user who wrote the review.
    • userId:这是外键,引用撰写评论的用户。
  • productId: This is the foreign key that references the product that the review is for.
    • productId:这是外键,引用评论所针对的产品。
  • rating: This is the rating that the user gave the product.
    • rating:这是用户给产品的评分。
  • title: This is the title of the review.
    • title:这是评论的标题。
  • description: This is the description of the review
    • description:这是评论的描述内容。
  • isVerifiedPurchase: This is a boolean that indicates whether the user has verified their purchase of the product.
    • isVerifiedPurchase:这是一个布尔值,表示用户是否已验证其购买该产品。
  • createdAt: This is the date and time that the review was created.
    • createdAt:这是评论创建的日期和时间。
  • product: This is the relation that links the review to the product.
    • product:这是将评论与产品关联的关系字段。
  • user: This is the relation that links the review to the user.
    • user:这是将评论与用户关联的关系字段。
Make sure in your User and Product models that you have a Review field like this:
请确保在你的 UserProduct 模型中包含如下 Review 字段:

Migration 迁移

Now we need to run the migration to create the Review table in the database.
现在我们需要运行迁移来在数据库中创建 Review 表。
Run the following commands:
运行以下命令:

Zod Schema Zod 模式

We are also going to need to create a Zod schema for inserting reviews.
我们还需要创建一个用于插入评论的 Zod 模式。
Open the lib/validator.ts file and add the following code:
打开 lib/validator.ts 文件并添加以下代码:
These are the fields and validation needed when we create a review.
这些是创建评论时所需的字段和验证规则。

Type 类型

Now we need to create a type for the review and use the zod schema to validate the data.
现在我们需要为评论创建一个类型,并使用 zod 模式来验证数据。
Open the types/index.ts file and add the following code:
打开 types/index.ts 文件并添加以下代码:
We are using all the fields from the insertReviewSchema and adding the id and createdAt fields as well as the user field.
我们使用了 insertReviewSchema 中的所有字段,并添加了 idcreatedAt 字段以及 user 字段。

Default Values Constant 默认值常量

Like with most of our forms, I am going to add a constant for the default values. Open the lib/constants/index.ts file and add the following code:
与我们的大多数表单一样,我将为默认值添加一个常量。打开 lib/constants/index.ts 文件并添加以下代码:
In the next lesson, we will start to create the rating and review functionality.
在下一课中,我们将开始创建评分和评论功能。

3. Review List Component 评论列表组件

We have quite a bit of work to do here. We need to create components for the review list and the review form. We need actions to get and post reviews and so on.
我们在这里有相当多的工作要做。我们需要为评论列表和评论表单创建组件。我们还需要获取和发布评论的操作等。
Let's start with the UI stuff first. I want to have a review-list component and in that we will display the reviews but we will also have a review-form component within the list component.
让我们先从UI部分开始。我想要一个review-list组件,在其中我们将显示评论,但我们还会在列表组件中有一个review-form组件。
So create a new file at app/(root)/product/[slug]/review-list.tsx and add the following code:
因此,在app/(root)/product/[slug]/review-list.tsx处创建一个新文件,并添加以下代码:
The component takes in the userId, productId and productSlug as props. We will use these to get the reviews for the product and to post reviews. For now, just log them out to the console.
该组件接收userIdproductIdproductSlug作为属性。我们将使用这些来获取产品的评论并发布评论。现在,只需将它们记录到控制台。
We want to add this to the product page. So open the app/(root)/product/[slug]/page.tsx file and import the following:
我们想将其添加到产品页面。因此,打开app/(root)/product/[slug]/page.tsx文件并导入以下内容:
We need to get the user ID. So add the following above the return in the ProductDetailsPage:
我们需要获取用户ID。因此,在ProductDetailsPage中的return上方添加以下内容:
Now under the closing </section> add the following section with the review list component:
现在,在闭合的</section>下方添加以下带有评论列表组件的部分:
Now if you go to the product page, you should see the component text and the console.log output.
现在,如果您转到产品页面,您应该会看到组件文本和console.log输出。
Let's bring in a few ShadCN components, icons and hooks into the ReviewList component:
让我们在ReviewList组件中引入一些ShadCN组件、图标和钩子:
Now let's just add an empty array in state to hold the reviews and then add the following to the return:
现在,让我们在状态中添加一个空数组来保存评论,然后在return中添加以下内容:
We are just checking for reviews and if the user is logged in, we will show the review form. Otherwise, we will show a message to sign in to write a review. We use the callbackUrl to redirect the user to the product page after they sign in.
我们只是在检查评论,如果用户已登录,我们将显示评论表单。否则,我们将显示登录以撰写评论的消息。我们使用callbackUrl在用户登录后将其重定向到产品页面。
Now you should just see the heading and no reviews message.
现在,您应该只会看到标题和暂无评论的消息。
Let's continue with the form in the next lesson.
让我们在下一课中继续处理表单。

4. Review Form Dialog Component

Display Review Form Dialog 显示评论表单对话框
I want to start on the review form before we actually try and fetch and display them because we need to actually have some reviews. So create a new file at app/(root)/product/[slug]/review-form.tsx and add the following code:
我想在我们实际获取和显示评论之前先开始处理评论表单,因为我们需要实际有一些评论。因此,在app/(root)/product/[slug]/review-form.tsx处创建一个新文件,并添加以下代码:
This component will be used to create a new review for the product. It will take in the userId, productId and a callback function to call when the review is submitted. I made the callback function optional for now because we are not going to add it yet.
该组件将用于为产品创建新评论。它将接收userIdproductId和一个在评论提交时调用的回调函数。 我暂时将回调函数设为可选,因为我们还不会添加它。
Let's add this to the review list component. Open the app/(root)/product/[slug]/review-list.tsx file and import the following:
让我们将其添加到评论列表组件中。打开app/(root)/product/[slug]/review-list.tsx文件并导入以下内容:
Now replace the comment with the component:
现在用组件替换注释:
You should see the text from the review form component.
您应该会看到评论表单组件的文本。
Now we will add a bunch of imports to the top of the file:
现在我们将在文件顶部添加一堆导入:
We have a bunch of ShadCN components, hooks, icons, zod schema, etc.
我们有一堆ShadCN组件、钩子、图标、zod模式等。
Let's add some code above the return:
让我们在return上方添加一些代码:
We are setting a state for the dialog to be open or closed. We are also getting the toast from the useToast hook. We are also creating a form with the useForm hook. We are also setting the resolver to the insertReviewSchema schema and the default values to the reviewFormDefaultValues constant.
我们正在设置对话框打开或关闭的状态。我们还从useToast钩子获取toast。我们还使用useForm钩子创建表单。我们还将解析器设置为insertReviewSchema模式,并将默认值设置为reviewFormDefaultValues常量。
Now let's create the dialog in the return:
现在让我们在return中创建对话框:
We have a title, description, rating, and a submit button. We are using a mix of ShadCN and React Hook Form components to create the form.
我们有标题、描述、评分和提交按钮。我们正在混合使用ShadCN和React Hook Form组件来创建表单。
When you click the button, the dialog will open. In the next lesson, we will start to add the logic to submit the form.
当您点击按钮时,对话框将打开。在下一课中,我们将开始添加提交表单的逻辑。

5. Create Update Review Action 创建和更新评论操作

Now that we have our form dialog, we need an action to submit the form to. Create a new file at lib/actions/review.actions.ts and add the following imports:
现在我们有了表单对话框,我们需要一个操作来提交表单。在lib/actions/review.actions.ts处创建一个新文件,并添加以下导入:
Now add the following action:
现在添加以下操作:
This is quite a bit of code, but it's very straightforward. We first validate the data using the insertReviewSchema and then check if the user has already reviewed the product. If they have, we update the review, otherwise we create a new one. We then get the average rating and number of reviews for the product and update the product's rating and number of reviews.
这是相当多的代码,但它非常直接。我们首先使用insertReviewSchema验证数据,然后检查用户是否已经评论过该产品。如果有,我们更新评论,否则创建一个新评论。然后我们获取产品的平均评分和评论数量,并更新产品的评分和评论数量。
In the next lesson, we will connect our form to this action.
在下一课中,我们将把表单连接到此操作。

6. Connect Review Form to Action 将评论表单连接到 Action

Now we have our form in the UI and we have the action to create or update a review. Let's connect the two.
现在我们在 UI 中有了表单,并且有了创建或更新评论的 action。让我们将两者连接起来。
Open the app/(root)/product/[slug]/review-form.tsx file and import the action:
打开 app/(root)/product/[slug]/review-form.tsx 文件并导入该 action:

Create Form Submit Handler 创建表单提交处理程序

Create the submit handler for the form:
为表单创建提交处理程序:
We are passing the productId and the values to the action. We are also setting the open state to false and calling the onReviewSubmitted function, which is passed into the form component. We are also showing a toast message with the response from the action.
我们将 productId 和值传递给 action。我们还将 open 状态设置为 false,并调用传递给表单组件的 onReviewSubmitted 函数。我们还会显示一个包含 action 响应的提示消息。
Remove the ? from the onReviewSubmitted? parameter in the form component.
从表单组件中的 onReviewSubmitted? 参数中移除 ?

Set User & Product ID 设置用户和产品 ID

In the handleOpenForm function, we need to add the following:
handleOpenForm 函数中,我们需要添加以下内容:
The form inputs only take care of the title, description and rating, but our validator needs the user and product ID. We set it when the form opens here.
表单输入只处理标题、描述和评分,但我们的验证器需要用户和产品 ID。我们在这里在表单打开时设置它们。

Add Handler To Form 将处理程序添加到表单

Add the following to the form tag:
将以下内容添加到 form 标签:

Pass in the callback to the <ReviewForm /> component 将回调传递给 <ReviewForm /> 组件

Open the app/(root)/product/[slug]/review-list.tsx file and pass in the following props to the <ReviewForm /> component:
打开 app/(root)/product/[slug]/review-list.tsx 文件并将以下 props 传递给 <ReviewForm /> 组件:
So when we submit the form the onReviewSubmitted function is called and the reload function is called. This will update the reviews list. For now, just add a console log.
因此,当我们提交表单时,会调用 onReviewSubmitted 函数,进而调用 reload 函数。这将更新评论列表。目前,只需添加一个 console log。
Create the reload function in the review-list.tsx file right above the return statement:
review-list.tsx 文件的 return 语句正上方创建 reload 函数:
Okay, we should be able to submit a review now. Go ahead and give it a try. Add a title, description, and rating.
好的,我们现在应该可以提交评论了。去试试吧。添加一个标题、描述和评分。
Now go to Prisma Studio and check the reviews table. You should see the review you just added.
现在转到 Prisma Studio 并检查 reviews 表。你应该能看到你刚添加的评论。

7. Get Reviews Action 获取评论 Action

Now we need to be able to get the reviews for a product. Let's open the lib/actions/review.actions.ts file and create a new action:
现在我们需要能够获取产品的评论。让我们打开 lib/actions/review.actions.ts 文件并创建一个新的 action:
We are simply getting all the reviews for a product and returning them. We are also including the user's name in the response.
我们只是获取产品的所有评论并返回它们。我们还在响应中包含了用户的姓名。

Get Review By User 按用户获取评论

Let's also create a function to get a product review for a specific product that was written by the current user:
让我们也创建一个函数来获取当前用户为特定产品撰写的评论:
We are taking in the productId, getting the session, and then finding the review for the productId and the userId.
我们接收 productId,获取会话,然后查找该 productId 和 userId 的评论。
Next, we will use these actions in the UI.
接下来,我们将在 UI 中使用这些 action。

8. Display Reviews 显示评论

We have our actions to get the reviews, now we need to fetch and display them in the ReviewList component.
我们有获取评论的 action,现在需要在 ReviewList 组件中获取并显示它们。
Open the app/(root)/products/[slug]/review-list.tsx file and import the getReviews action:
打开 app/(root)/products/[slug]/review-list.tsx 文件并导入 getReviews action:
Now we need to fetch the reviews. We will do this in the useEffect hook.
现在我们需要获取评论。我们将在 useEffect hook 中完成这个操作。
Add the following code under the state values:
在状态值下方添加以下代码:
We are calling the getReviews action and setting the reviews state with the response. We are also using the useEffect hook to call the loadReviews function when the component mounts.
我们调用 getReviews action 并使用响应设置 reviews 状态。我们还使用 useEffect hook 在组件挂载时调用 loadReviews 函数。

Show Reviews 显示评论

Now let's map over the reviews and display them where the comment "REVIEWS HERE" is:
现在让我们遍历评论并在注释 "REVIEWS HERE" 所在的位置显示它们:

Rating Component 评分组件

The rating component is going to have some SVG stars. Obviously we are not going to type out all the SVG, so I will attach the component file to this lesson. You can also get it from the final repository. It will go in components/shared/product/rating.tsx.
评分组件将包含一些 SVG 星星。显然我们不会手动输入所有的 SVG,所以我将把组件文件附加到这节课中。你也可以从最终仓库中获取它。它将放在 components/shared/product/rating.tsx 中。
Here is the code:
这是代码:
Now, import the Rating component into the ReviewList component:
现在,将 Rating 组件导入到 ReviewList 组件中:
Replace the // RATING HERE with the following:
用以下内容替换 // RATING HERE
Now you should see the rating component.
现在你应该能看到评分组件了。
We also want to use this on the product card and product page.
我们还想在产品卡片和产品页面上使用它。

Product Page Update 产品页面更新

Open the app/(root)/product/[slug]/page.tsx file and import the Rating component:
打开 app/(root)/product/[slug]/page.tsx 文件并导入 Rating 组件:
Replace this line:
替换这一行:
with this line:
用这一行:

Update Product Card 更新产品卡片

Now open the components/shared/product/product-card.tsx file and import the Rating component:
现在打开 components/shared/product/product-card.tsx 文件并导入 Rating 组件:
Replace this line:
替换这一行:
with this line:
用这一行:
In the next lesson, we will handle reloading the reviews and the update.
在下一节课中,我们将处理评论的重新加载和更新。

9. Update & Reload Reviews 更新并重新加载评论

Now we need to make it so that if the user already has a review, it will show in the form and the user can update it. We also want to reload the reviews so that the user can see the added or updated review. 现在我们需要实现这样的功能:如果用户已经有评论,它将在表单中显示,用户可以更新它。我们还想要重新加载评论,以便用户可以看到添加或更新的评论。
Open the app/(root)/product/[slug]/review-form.tsx file and import the getReviewByProductId action: 打开 app/(root)/product/[slug]/review-form.tsx 文件并导入 getReviewByProductId 动作:
Now, in the handleOpenForm function, add the following: 现在,在 handleOpenForm 函数中,添加以下内容:
We are setting the productId and userId in the form, then we are getting the review by productId and if it exists, we are setting the form values to the review values. 我们在表单中设置 productId 和 userId,然后通过 productId 获取评论,如果评论存在,我们将表单值设置为评论值。
You should see the data in the form if you already have a review for that product. 如果您已经对该产品有评论,您应该在表单中看到数据。

Reload Reviews 重新加载评论

Now go to the app/(root)/product/[slug]/review-list.tsx file and add the following to the reload() function: 现在转到 app/(root)/product/[slug]/review-list.tsx 文件,并向 reload() 函数中添加以下内容:
You will also need to initialize the toast. Add this under the state: 您还需要初始化 toast。在状态下方添加此内容:
Now try and edit or add a review and you should see the reviews reload. 现在尝试编辑或添加评论,您应该会看到评论重新加载。

📎 参考文章

  • 一些引用
  • 引用文章
 
💡
欢迎您在底部评论区留言,一起交流~
上一篇
第一节 大脑:重新认识你自己
下一篇
Harness Engineering - 搭建Mini Harness
Loading...