You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:17:52