基于Next.js+TypeScript+Prisma+Postgres实现分类动态页面:按ID查商品
基于Prisma+Postgres+Next.js+TypeScript实现分类动态商品页面问题
问题背景
我使用Prisma + Postgres、Next.js和TypeScript技术栈,需要创建一个分类动态页面,展示与该分类ID关联的商品(Item)。我知道需要在分类相关组件中导出异步函数,但不清楚具体实现逻辑,且无法关联分类动态页面的slug。
数据模型Schema
model Item { id Int @id @default(autoincrement()) slug String title String about String category Category @relation(fields: [categoryId], references: [id]) categoryId Int img_path String } model Category { id Int @id @default(autoincrement()) slug String title String items Item[] img_path String }
现有Prisma操作函数
import prisma from './client'; export async function getCategories() { try { const categoriesFound = await prisma.category.findMany(); return { categoriesFound }; } catch (error) { return { error }; } } export async function getItemsForCategories() { try { const itemsForCategory = await prisma.category.findMany({ where: { items: { every: { id: 1, }, }, }, }); return { itemsForCategory }; } catch (error) { return { error }; } } export async function createCategory(category: any) { try { const categoryCreated = await prisma.category.create({ data: category }); return { category: categoryCreated }; } catch (error) { return { error }; } } export async function getCategoryById(id: number) { try { const category = await prisma.category.findUnique({ where: { id } }); return { category }; } catch (error) { return { error }; } } export async function deleteCategories() { try { const deleteCategories = await prisma.category.deleteMany(); } catch (error) { return { error }; } } export async function updateCategory(id: number, data: any) { try { const updatedCategory = await prisma.category.update({ where: { id }, data, }); return { category: updatedCategory }; } catch (error) { return { error }; } }
现有API请求代码
import { NextApiRequest, NextApiResponse } from 'next'; import { createCategory, getCategories, getCategoryById, deleteCategories, updateCategory, } from '@/prisma/categories'; export default async function handler( req: NextApiRequest, res: NextApiResponse ): Promise<void> { if (req.method === 'GET') { try { const categories = await getCategories(); res.status(200).json({ categories }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); } } else if (req.method === 'POST') { try { const data = req.body; const category = await createCategory(data); res.status(201).json({ category }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); } } else if (req.method === 'DELETE') { try { await deleteCategories(); return res.status(203).json({ message: 'Categories deleted' }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); } } else if (req.method === 'PATCH') { try { const { id, data } = req.body; const updatedCategory = await updateCategory(id, data); res.status(200).json({ item: updatedCategory }); } catch (error) { res.status(500).json({ error: 'Internal server error' }); } } else { res.status(404).json({ error: 'Not found' }); } }
尝试的无效函数
我尝试编写了如下函数,但无法关联分类的动态页面slug:
export async function getItemsForCategories() { try { const itemsForCategory = await prisma.category.findMany({ where: { items: { every: { categoryId: 1, }, }, }, }); return { itemsForCategory }; } catch (error) { return { error }; } }
解决方案
1. 修正Prisma查询函数
新增一个根据slug查询分类及其关联商品的函数,替换原有无效的getItemsForCategories:
export async function getCategoryBySlug(slug: string) { try { const category = await prisma.category.findUnique({ where: { slug }, include: { items: true }, // 关联查询该分类下的所有商品 }); return { category }; } catch (error) { return { error }; } }
2. 实现Next.js动态页面(App Router)
在app/categories/[slug]/page.tsx创建动态页面组件:
import { getCategoryBySlug, getCategories } from '@/prisma/categories'; // 预先生成所有分类的动态路由(静态生成) export async function generateStaticParams() { const { categoriesFound } = await getCategories(); return categoriesFound.map((category) => ({ slug: category.slug, })); } // 页面组件 export default async function CategoryPage({ params }: { params: { slug: string } }) { const { category, error } = await getCategoryBySlug(params.slug); if (error || !category) { return <div>分类不存在或加载失败</div>; } return ( <div> <h1>{category.title}</h1> <div className="items-grid"> {category.items.map((item) => ( <div key={item.id} className="item-card"> <img src={item.img_path} alt={item.title} /> <h3>{item.title}</h3> <p>{item.about}</p> </div> ))} </div> </div> ); }
3. 实现Next.js动态页面(Pages Router)
如果使用Pages Router,在pages/categories/[slug].tsx创建组件:
import { GetStaticProps, GetStaticPaths } from 'next'; import { getCategoryBySlug, getCategories } from '@/prisma/categories'; type CategoryPageProps = { category: Awaited<ReturnType<typeof getCategoryBySlug>>['category']; }; export default function CategoryPage({ category }: CategoryPageProps) { if (!category) { return <div>分类不存在</div>; } return ( <div> <h1>{category.title}</h1> <div className="items-grid"> {category.items.map((item) => ( <div key={item.id} className="item-card"> <img src={item.img_path} alt={item.title} /> <h3>{item.title}</h3> <p>{item.about}</p> </div> ))} </div> </div> ); } // 生成所有动态路由路径 export const getStaticPaths: GetStaticPaths = async () => { const { categoriesFound } = await getCategories(); const paths = categoriesFound.map((category) => ({ params: { slug: category.slug }, })); return { paths, fallback: false }; }; // 获取页面数据 export const getStaticProps: GetStaticProps<CategoryPageProps> = async ({ params }) => { const slug = params?.slug as string; const { category } = await getCategoryBySlug(slug); return { props: { category }, }; };
4. 优化TypeScript类型(可选)
将原有函数中的any替换为具体类型,提升类型安全性:
import { Category } from '@prisma/client'; export async function createCategory(category: Omit<Category, 'id' | 'items'>) { try { const categoryCreated = await prisma.category.create({ data: category }); return { category: categoryCreated }; } catch (error) { return { error }; } } export async function updateCategory(id: number, data: Partial<Omit<Category, 'id' | 'items'>>) { try { const updatedCategory = await prisma.category.update({ where: { id }, data, }); return { category: updatedCategory }; } catch (error) { return { error }; } }
内容的提问来源于stack exchange,提问作者petechka
相关产品推荐
相关产品推荐

