iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 24
0
Modern Web

React + GraphQL 全端練習筆記系列 第 24

仿Trello - Prisma 安裝與 Schema 建立

  • 分享至 

  • xImage
  •  

本系列文以製作專案為主軸,紀錄小弟學習React以及GrahQL的過程。主要是記下重點步驟以及我覺得需要記憶的部分,有覺得不明確的地方還請留言多多指教。

前篇提到簡化的Prisma介面建立步驟大致如下:

  1. 撰寫Schema,或從頭建立或根據既有資料庫Model自動生成
  2. 根據Schem自動生成Prisma Client,提供CRUD方法

這次就來從頭建立Schema開始,簡單串接一個sqlite資料庫。

Prisma 安裝

npm安裝Prisma CLI:

npm install @prisma/cli --save-dev

Prisma Client,Prisma Migration等工具都包含在這了,之後可以用指令呼叫這些功能。

Prisma Schema

server中建立prisma資料夾,然後建立schema.prisma:

/* 
/server/prisma/schema.prisma
*/
datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

generator client {
  provider = "prisma-client-js"
}

model List {
  id    Int     @default(autoincrement()) @id
  title  String
  todos  Todo[]
}

model Todo {
  id        Int     @default(autoincrement()) @id
  name     String
  list    List  @relation(fields: [listId], references: [id])
  listId  Int
}

Schema的內容待會介紹,目前看到的文字應該是單色的,因為編譯器認不得.prisma這個檔名,如果用的是VS Code,可以安裝Prisma套件幫助標色與自動完成。

接著依序介紹Prisma Schema的構成。

Data source

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}
  • provider: 資料庫的種類,目前可選擇 postgresql, mysql或 sqlite
  • url: 資料庫的連結

url部分因為這次打算用專案目錄底下的sqlite db,所以用file指定db檔案的路徑。

正式的話就是帶上資料庫的連結

datasource db {
  provider = "postgresql"
  url      = "postgresql://johndoe:mypassword@localhost:5432/mydb?schema=public"
}

或是用env指定連結

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

Generator

generator client {
  provider = "prisma-client-js"
}

主要定義當用CLI執行 prisma generate指令時的行為。

  • provider: 生成 Prisma Client時使用的模板,目前只有prisma-client-js

可以追加參數:

  • output : 指定生成的prisma client資料夾路徑,預設在node_modules/@prisma/client

Data model

model List {
  id    Int     @default(autoincrement()) @id
  title  String
  todos  Todo[]
}

model Todo {
  id        Int     @default(autoincrement()) @id
  name     String
  list    List  @relation(fields: [listId], references: [id])
  listId  Int
}

資料模型,也就是資料庫的參照,跟GraphQL Schema的觀念一樣,一個model type底下有數個field,field也分為 scalar fields 跟 relation fields。

詳細介紹fields前,先介紹兩個特殊標記

  • []
    中括弧跟GrpahQL一樣表示欄位對應資料陣列,不同的是,標記加在Type後面。
    todos  Todo[]
    
  • ?
    Prisma預設所有欄位都是必要的,若有欄位是可選欄位,則在Type後面加上問號。
    (跟GraphQL相反,GraphQL加上驚嘆號是必要欄位)
    list List?
    

回頭介紹欄位種類:

  • Scalar fields: 純量資料欄位,像是Int,String,Boolean,Float還有 DateTime。

    可以在每個field之後加上屬性標籤,舉例一些標籤:

    @id :標記欄位為一個model的主鍵,通常會搭配autoincrement自動遞增

    @unique :標記該欄位必須為獨特值,像是email

    @default : 標記該欄位的預設值,像是

    • @default(autoincrement()) :每次新增資料時遞增
    • @default(now()) : 預設值為目前的時間
    • @defalut(false) : 預設為false

    @relation : 定義與其他model的關聯,接著會介紹。

  • Relation fields: 表示不同Model間關聯的欄位,筆記幾個常用關聯的寫法:

    1. 單對單
      借用官方範例,一位User有一個Profile:
    model User {
      id        Int       @id @default(autoincrement())
      profile   Profile?
    }
    model Profile {
      id      Int    @id @default(autoincrement())
      user    User   @relation(fields: [userId], references: [id])
      userId  Int   
    }
    

    注意這兩行:

    user    User   @relation(fields: [userId], references: [id])
    userId  Int   
    

    在Profile中建立了外鍵userId的欄位,並在user關聯欄位上用 @relation指明userId對應的是 User model裡的 id欄位,就形成關聯了。

    relation中,fields為自己的欄位,關聯到references,也就是目標model的欄位。

    1. 單對多
      借用官方範例,一位User有多個Post:
    model User {
      id        Int      @id @default(autoincrement())
      posts     Post[]
    }
    model Post {
      id        Int   @id @default(autoincrement())
      author    User  @relation(fields: [authorId], references: [id])
      authorId  Int
    }
    

    跟單對單時相同,Post建立authorId並指定relation關聯,只是在User側的post欄位對應的是Post陣列。

    Trello專案的List跟Todo也是這種關聯方式。

    1. 多對多
      借用官方範例,一個Post有多個Category,一個Category有多個Post:
    model Post {
      id         Int        @id @default(autoincrement())
      categories Category[]
    }
    model Category {
      id    Int    @id @default(autoincrement())
      posts Post[]
    }
    

    在兩個model各自宣告資料陣列欄位,就形成多對多關係,單純這樣宣告Prisma就會幫助建立relation table,以雙方model的id關聯。


    (圖片來源: Prisma)

    然後在生成Prisma Client後可以用connect()宣告資料間的關聯,之後會介紹。

    如果想要relation table帶有更多資訊,像是createdAt,也可以自行宣告:

    model Post {
      id         Int                 @id @default(autoincrement())
      title      String
      categories CategoriesOnPosts[]
    }
    model Category {
      id    Int                 @id @default(autoincrement())
      name  String
      posts CategoriesOnPosts[]
    }
    
    //自行宣告relation model
    model CategoriesOnPosts {
      post        Post     @relation(fields: [postId], references: [id])
      postId      Int      
      category    Category @relation(fields: [categoryId], references: [id])
      categoryId  Int      
      createdAt   DateTime @default(now()) //增加自選欄位
      @@id([postId, categoryId])
    }
    
    1. 自相關
      自相關又分為單對單、單對多、單對單的自相關,篇幅關係就不列舉了,可以到官方文件查閱。
      這邊以單對單自相關做代表,借用官方範例,一個使用者有多位前任或繼任:
    model User {
      id             Int     @default(autoincrement()) @id
      name           String?
      successorId Int?
      successor   User?   @relation("BlogOwnerHistory", fields: [successorId], references: [id])
      predecessor User?   @relation("BlogOwnerHistory")
    }
    

    需要新增一個外鍵(successorId)做關聯用,並且relation需要多加一個字串命名這個關聯,並且關聯的欄位要用同一個名稱,畢竟可能同時有多個自相關關係。

生成 Prisma Client

Schema寫好後可以到指令視窗輸入

npx prisma generate

接著就會解析Schema並生成Prisma Client,成功編譯的話會在node_modules/@prisma/client (預設路徑) 找到生成的程式。

不過目前沒有資料庫供操作,留待明天用Prisma Migration生成資料庫表單並撰寫seeder吧。

References:


上一篇
Prisma 簡介
下一篇
仿Trello - Prisma Migrate 與添加Seeder
系列文
React + GraphQL 全端練習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言