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

相关推荐
小小竹子2 分钟前
前端vue-实现富文本组件
前端·vue.js·富文本
小白小白从不日白11 分钟前
react hooks--useReducer
前端·javascript·react.js
下雪天的夏风23 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom35 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
wn53140 分钟前
【Go - 类型断言】
服务器·开发语言·后端·golang
Hello-Mr.Wang1 小时前
vue3中开发引导页的方法
开发语言·前端·javascript
希冀1231 小时前
【操作系统】1.2操作系统的发展与分类
后端
程序员凡尘1 小时前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
GoppViper2 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理
爱上语文3 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring