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

相关推荐
EricWang135814 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning14 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人24 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
假装我不帅1 小时前
asp.net framework从webform开始创建mvc项目
后端·asp.net·mvc
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
神仙别闹1 小时前
基于ASP.NET+SQL Server实现简单小说网站(包括PC版本和移动版本)
后端·asp.net
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程1 小时前
【前端基础】CSS基础
前端·css
嚣张农民1 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
计算机-秋大田1 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue