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

相关推荐
博客zhu虎康9 分钟前
ElementUI 的 form 表单校验
前端·javascript·elementui
敲啊敲952737 分钟前
5.npm包
前端·npm·node.js
小池先生43 分钟前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端
brrdg_sefg1 小时前
Rust 在前端基建中的使用
前端·rust·状态模式
m0_748230941 小时前
Rust赋能前端: 纯血前端将 Table 导出 Excel
前端·rust·excel
qq_589568101 小时前
Echarts的高级使用,动画,交互api
前端·javascript·echarts
小蜗牛慢慢爬行2 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
黑客老陈2 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
正小安2 小时前
Vite系列课程 | 11. Vite 配置文件中 CSS 配置(Modules 模块化篇)
前端·vite