Prisma 自引用与菜单实战

一、什么是 Primsa 自引用?

"自引用" 通常指的是一个数据结构或对象引用自身的情况。

在数据库模型中,自引用可能指的是在一个表中的某一列引用了同一表中的其他行的数据,通常用来表示层次结构。

二、自引用解决了哪些问题?

  • 菜单(多级菜单、动态菜单、权限管理)
  • 评论与回复
  • 组织结构
  • 目录结构
  • 分类系统
  • 社交关系
  • 有向图

三、菜单示例

本示例一 sqlite 为例, 包含一个菜单项目的 Schema 文件

schema 复制代码
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model MenuItem {
  id        Int      @id @default(autoincrement())
  label     String
  parent    MenuItem? @relation("ChildToParent", fields: [parentId], references: [id])
  parentId  Int?
  children  MenuItem[] @relation("ChildToParent")
}

四、解析

此 Schema 只有一个 label 是实际字段,假设它保存就是 路由路径

  • children 字段关联 MenuItem 类型也就是它自己
  • parent 字段通过 parentId 与自己关联

由此形成了一个简单的 自引用关系,其中包含了 父与子 是一对多的关系,而 子与父 是多对一的关系。

五、制作模型工具

ts 复制代码
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

prisma.$connect().then((err) => {
  console.log(err);
});

export { prisma };

六、创建菜单根路由

ts 复制代码
const createMenuItemRoot = async () => {
  const menuItem = await prisma.menuItem.create({
    data: {
      label: '/',
    },
  });
  console.log('created menu items ', menuItem);
};

createMenuItemRoot()

在数据库中插入一条路由的顶层数据,也就是根路由。

七、创建子路由

ts 复制代码
const createMenuItem = async (label, parentId) => {
  const menuItem = await prisma.menuItem.create({
    data: {
      label,
      parentId,
    },
  });
  console.log('created menu items ', menuItem);
};

八、创建子路由示例

创建三个一级菜单:

ts 复制代码
createMenuItem('/dashboard', 1); // id 2
createMenuItem('/analysis', 1);
createMenuItem('/monitor', 1);

给 dashboard 路由创建子菜单

ts 复制代码
createMenuItem('/analysis/p1', 2);
createMenuItem('/analysis/p2', 2);

得到的表的结构:

九、获取列表

ts 复制代码
const findAll = async () => {
  const menus = await prisma.menuItem.findMany();
  console.log(menus);
};

十、深层次查找

使用 prisma 进行深层次查找(也就是查找 children):

ts 复制代码
const findChildren = async () => {
  const menus = await prisma.menuItem.findMany({
    where: {
      id: 1,
    },
    include: {
      children: {
        where: {
          parentId: 1,
        },
        include: {
          children: {
            where: {
              parentId: 2,
            },
          },
        },
      },
    },
  });
  console.log(menus[0].children);
};

十一、根据 id 查询所有的子菜单

使用 递归 的方式,查询菜单的所有菜单。

ts 复制代码
async function getMenus(menuId) {
  const node = await prisma.menuItem.findUnique({
    where: { id: menuId },
    include: {
      children: {
        where: {
          parentId: menuId,
        },
      },
    },
  });

  if (!node) {
    return {};
  }

  if (node.children.length > 0) {
    for (const child of node.children) {
      const childNodes = await getMenus(child.id);
      child.children = childNodes;
    }
  }

  return node;
}

十二、获取子引用表中的菜单

ts 复制代码
getMenus(1) // 顶级

当能够方便的查询到树状结构的数据的时候,我们就能方便的展示在管理系统的权限管理中。

十三、小结

本文主要讲解Prsima 的自引用的模型以及关于菜单使用方式。自引用解决了类似树状数据结构问题,以菜单为例,当我们需要查询一个角色的对应的菜单的时候,此时自引用数据结构就会变得非常有用。

相关推荐
Csvn4 小时前
OpenSpec 详细使用教程
前端
明月_清风4 小时前
加密解密系统完全指南:原理剖析与 Go 实践
后端
之歆5 小时前
Day19_LESS 完全指南——从入门到工程实践
前端·css·less
小江的记录本5 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
云水一下6 小时前
HTML5 从入门到精通:实战收官——从零搭建完整静态网站,综合运用所有知识
前端·html5
不总是6 小时前
Windows 系统 Node.js 免安装版(zip)安装与配置教程(2026 最新)
前端·windows·node.js
冬奇Lab6 小时前
每日一个开源项目(第105篇):Twenty - 跳出 Salesforce 的圈套,定义现代开源 CRM
前端·后端·开源
zhangyao9403307 小时前
开发pc端时,表格的高度怎么设置才能铺满页面
前端·javascript·elementui
ServBay7 小时前
月之暗面 Kimi Code 0.4.0 发布,终端 AI 编码助手全面采用 TypeScript,实现毫秒级启动
后端·aigc·ai编程
小江的记录本7 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试