2022 年 4 月 28 日

使用 Remix、Prisma 和 MongoDB 建立全端應用程式:參考完整性與圖片上傳

12 分鐘閱讀

歡迎來到本系列的第四篇文章,您將在此學習如何從頭開始使用 MongoDB、Prisma 和 Remix 建立全端應用程式!在這一部分中,您將建置應用程式的個人資料設定區塊,包括圖片上傳元件,並設定您的 schema 以在資料中提供參考完整性。

Build A Fullstack App with Remix, Prisma & MongoDB: Referential Integrity & Image Uploads

目錄

簡介

在本系列的前一部分中,您建立了此應用程式的主要區域,包括讚譽牆、使用者列表、最近的讚譽列表和發送讚譽表單。

在這一部分中,您將完成此應用程式的開發,方法是為使用者建立更新其個人資料資訊和上傳個人資料圖片的方式。您還將對您的 schema 進行一些變更,這些變更將為您的資料庫提供參考完整性。

注意:此專案的起點可在 GitHub 儲存庫的 part-3 分支中找到。如果您想查看此部分的最終結果,請前往 part-4 分支。

開發環境

為了跟上提供的範例,您需要...

注意:選用的擴充功能為 Tailwind 和 Prisma 增加了一些非常棒的 intellisense 和語法突顯功能。

建立個人資料設定模組

應用程式的個人資料設定頁面將顯示在一個模組中,該模組可透過點擊頁面右上角的個人資料設定按鈕來存取。

app/components/search-bar.tsx

  • 為匯出的元件新增一個名為 profile 的 prop,其類型為 Prisma 產生的 Profile 類型
  • 匯入 UserCircle 元件。
  • form 的內容末尾呈現 UserCircle 元件,並傳遞新的 profile prop 資料。這將作為您的個人資料設定按鈕。

如果您的開發伺服器已經在執行,這將導致您的首頁拋出錯誤,因為 SearchBar 元件現在期望有個人資料資料。

app/routes/home.tsx 檔案中,使用在本系列的第二部分中從 app/utils/auth.server.ts 撰寫的 getUser 函數。使用此函數在 loader 函數內部載入已登入使用者的資料。然後將該資料提供給 SearchBar 元件。

您的 SearchBar 現在將可以存取它需要的 profile 資料。如果您之前因為缺少此資料而收到錯誤,則重新整理瀏覽器中的頁面應會顯示個人資料按鈕已在頁面右上角成功呈現。

建立模組

目標是在點擊個人資料設定按鈕時開啟個人資料設定模組。與本系列前一部分中建立的讚譽模組類似,您需要設定一個巢狀路由,您將在其中呈現新的模組。

app/routes/home 中新增一個名為 profile.tsx 的檔案,並包含以下內容以開始

上面的程式碼片段...

  • ... 將模組呈現到新的 ProfileSettings 元件中。
  • ... 在 loader 函數中檢索並傳回已登入使用者的資料。
  • ... 使用 useLoaderData hook 來存取從 loader 函數傳回的 user 資料。

為了開啟這個新的模組,請在 app/components/search-bar.tsx 中為 UserCircle 元件新增一個 onClick 處理常式,該處理常式使用 Remix 的 useNavigate hook 將使用者導航到 /home/profile 子路由。

如果您現在點擊個人資料設定按鈕,您應該會在螢幕上看到新的模組顯示。

建立表單

您將建立的表單將具有三個欄位,允許使用者修改其個人資料詳細資訊:名字、姓氏和部門。

首先新增名字和姓氏輸入欄位來建立表單

以下是上面新增內容的概述

  1. 新增變更中需要的匯入。
  2. 在 state 中建立一個 formData 物件,其中包含表單的值。這會將這些值預設為已登入使用者現有的個人資料資料。
  3. 建立一個函數,該函數接收 HTML change 事件和欄位名稱作為參數。這些參數用於在元件中輸入欄位的值變更時更新 formData state。
  4. 呈現表單的基本佈局以及兩個輸入欄位。

此時,沒有錯誤處理機制,表單也不會執行任何操作。在新增這些部分之前,您需要新增部門下拉式選單。

app/utils/constants.ts 中,新增一個新的 departments 常數,以保存 Prisma schema 中定義的可能選項。將以下匯出新增到該檔案

departments 匯入到您的 app/routes/home/profile.tsx 檔案中,以及 SelectBox 元件,並使用它們將新的輸入新增到您的表單中

此時,您的表單應呈現正確的輸入及其選項。它會將其值預設為與已登入使用者個人資料相關聯的目前值。

允許使用者提交表單

您將建立的下一個部分是 action 函數,它將使此表單正常運作。

在您的 app/routes/home/profile.tsx 中,新增一個 action 函數,該函數從 request 物件中檢索表單資料,並驗證 firstNamelastNamedepartment 欄位

上面的 action 函數執行以下操作

  1. request 物件中提取您需要的表單資料點。
  2. 確保您關心的每筆資料都是 string 資料類型。
  3. 使用先前撰寫的 validateName 函數驗證資料。
  4. 重新導向到 /home 路由,關閉設定模組。

上面的程式碼片段也會在各種驗證失敗時拋出相關錯誤。為了使用經過驗證的資料,請撰寫一個函數,讓您可以更新使用者。

app/utils/user.server.ts 中,匯出以下函數

此函數可讓您傳入任何 profile 資料,並更新 id 與提供的 userId 相符的使用者。

回到 app/routes/home/profile.tsx 檔案中,匯入該新函數並使用它在 action 函數中更新已登入使用者

現在當使用者點擊儲存按鈕時,他們更新的個人資料資料將被儲存,並且模組將關閉。

新增圖片上傳元件

設定 AWS 帳戶

您的使用者現在可以更新其個人資料中的一些重要資訊,但是,最好新增一項功能,讓使用者可以設定個人資料圖片,以便其他使用者更容易識別他們。

為此,您將設定一個 AWS S3 檔案儲存儲存桶來保存上傳的圖片。如果您還沒有 AWS 帳戶,可以在這裡註冊。

注意:Amazon 提供免費方案,讓您可以免費存取 S3。

建立 IAM 使用者

擁有帳戶後,您需要在 AWS 中設定 身分與存取管理 (IAM) 使用者,以便您可以產生存取金鑰 ID私密金鑰,這兩者都是與 S3 互動所必需的。

注意:如果您已經擁有 IAM 使用者及其金鑰,請隨意跳過。

前往 AWS 主控台首頁。在頁面的右上角,點擊標籤為您的使用者名稱的下拉式選單,然後選取 安全憑證

進入該區塊後,點擊左側選單中存取管理下的使用者選項。

在此頁面上,點擊頁面右上角的新增使用者按鈕。

這將引導您完成一個簡短的精靈,讓您可以設定您的使用者。請按照以下步驟操作

第一個區塊要求

  1. 使用者名稱:提供任何使用者名稱。
  2. 選取 AWS 存取類型:選取存取金鑰 - 程式化存取選項,這會啟用存取金鑰 ID私密金鑰的產生。

在精靈的第二步中,進行以下選取

  1. 選取「直接附加現有政策」選項。
  2. 搜尋詞彙「S3」。
  3. 點擊標籤為AmazonS3FullAccess的選項旁邊的核取方塊。
  4. 點擊表單底部的「下一步」。

如果您想為您的使用者新增標籤以幫助更輕鬆地管理和組織您帳戶中的使用者,請在此精靈的第三步中新增這些標籤。完成此頁面後,點擊下一步

如果此頁面上的摘要看起來不錯,請點擊頁面底部的建立使用者按鈕。

點擊該按鈕後,您將進入一個包含您的存取金鑰 ID私密金鑰的頁面。複製這些金鑰並將它們儲存在您可以輕鬆存取的地方,因為您很快就會使用它們。

設定 S3 儲存桶

現在您已經擁有使用者和存取金鑰,請前往 AWS S3 儀表板,您將在其中設定檔案儲存儲存桶。

在此頁面的右上角,點擊建立儲存桶按鈕。

系統會要求您提供儲存桶的名稱和區域。填寫這些詳細資訊,並將您選擇的值與您先前儲存的存取金鑰 ID私密金鑰一起儲存。您稍後也需要這些資訊。

填寫完畢後,點擊表單最底部的建立儲存桶

儲存桶建立完成後,您將被傳送到儲存桶儀表板頁面的物件標籤。導航到權限標籤。

在此標籤中,點擊封鎖公開存取區段下的編輯按鈕。在此表單中,取消選取封鎖所有公開存取方塊,然後點擊儲存變更。這會將您的儲存桶設定為公開,這將允許您的應用程式存取圖片。

在該區段下方,您會看到儲存桶政策區段。貼上以下政策,並確保將 <bucket-name> 替換為您的儲存桶名稱。此政策將允許您的圖片公開讀取

您現在已經設定了您的 AWS 使用者和 S3 儲存桶。接下來,您需要將金鑰和儲存桶設定儲存到您的 .env 檔案中,以便稍後可以使用它們。

更新您的 Prisma schema

您現在將在您的資料庫中建立一個欄位,您將在其中儲存上傳圖片的連結。這些應與 Profile 嵌入式文件一起儲存,因此請在您的 Profile 類型區塊中新增一個新欄位。

要使用這些變更更新 Prisma Client,請執行 npx prisma generate

建立圖片上傳元件

app/components 中建立一個名為 image-uploader.tsx 的新檔案,並包含以下內容

上面的程式碼片段是完整的圖片上傳元件。以下是正在發生的事情的概述

  1. 定義了一個 preventDefault 函數來處理元件中檔案輸入的變更。
  2. 定義了一個 handleDrop 函數來處理元件中檔案輸入上的 drop 事件。
  3. 定義了一個 handleChange 函數來處理元件中檔案輸入上的任何 change 事件。
  4. 呈現了一個 div,其中定義了各種事件處理常式,使其能夠對檔案拖放、拖曳事件和點擊做出反應。這些用於觸發圖片上傳和樣式變更,這些變更僅在元素接收拖曳事件時才會出現。

每當此元件中 input 的值變更時,就會呼叫 props 中的 onChange 函數,並傳遞檔案資料。該資料將上傳到 S3。

接下來,建立將處理圖片上傳的服務。

建立圖片上傳服務

為了建立您的圖片上傳服務,您將需要兩個新的 npm 套件

  • aws-sdk:公開一個 JavaScript API,讓您可以與 AWS 服務互動。
  • cuid:一個用於產生唯一 ID 的工具。您將使用它來產生隨機檔案名稱。

您的圖片上傳服務將存在於一個新的實用程式檔案中。在 app/utils 中建立一個名為 s3.server.ts 的檔案。

為了處理上傳,您將使用 Remix 的 unstable_parseMultipartFormData 函數,該函數處理 request 物件的 multipart/form-data 值。

注意multipart/form-data 是在表單中張貼整個檔案時的表單資料類型。

unstable_parseMultipartFormData 將接收兩個參數

  1. 從表單提交中檢索的 request 物件。
  2. 一個 uploadHandler 函數,它會串流檔案資料並處理上傳。

注意unstable_parseMultipartFormData 函數的使用方式與我們過去使用的 Remix 的 request.formData 函數類似。

將以下函數和匯入新增到您建立的新檔案中

此程式碼設定您的 S3 API,以便您可以與您的儲存桶互動。它還新增了 uploadHandler 函數。此函數

  1. 使用您在設定 AWS 使用者和 S3 儲存桶時儲存的環境變數來設定 S3 SDK。
  2. 只要資料金鑰的名稱為 'profile-pic',就從 request 串流檔案資料。
  3. 將檔案上傳到 S3。
  4. 傳回 S3 傳回的 Location 資料,其中包含 S3 中新檔案的 URL 位置。

現在 uploadHandler 已完成,新增另一個函數,該函數實際接收 request 物件,並將其與 uploadHandler 一起傳遞到 unstable_parseMultipartFormData 函數中。

此函數會傳遞一個 request 物件,該物件稍後將從 action 函數傳送過來。

檔案資料會透過 uploadHandler 函數傳遞,該函數處理上傳到 S3,而 formData 會在表單資料物件中傳回新檔案的位置。'profile-pic' URL 隨後會從該物件中提取並由函數傳回。

使用元件與服務

現在,實作可運作的個人資料圖片上傳所需的兩個部分都已完成,將它們放在一起。

透過在 app/routes 中建立一個名為 avatar.ts 的新檔案,並包含以下 action 函數,來新增一個處理上傳表單資料的資源路由

上面的函數執行以下步驟來處理上傳表單

  1. 抓取請求使用者的 id
  2. 上傳在請求資料中傳遞的檔案。
  3. 使用新的 profilePicture URL 更新請求使用者的個人資料資料。
  4. 使用 imageUrl 變數回應 POST 請求。

現在您可以使用 ImageUploader 元件來處理檔案上傳,並將檔案資料傳送到這個新的 /avatar 路由。

app/routes/home/profile.tsx 中,匯入 ImageUploader 元件並將其新增到表單中,位於輸入欄位的左側。

另外新增一個函數來處理 ImageUploader 元件發出的 onChange 事件,並在 formData 變數中新增一個新欄位來儲存個人資料圖片資料。

現在,如果您前往該表單並嘗試上傳檔案,資料應正確儲存在 S3、資料庫和表單的 state 中。

顯示個人資料圖片

太棒了!圖片上傳運作順利,現在您只需要在網站上任何顯示使用者頭像的地方顯示這些圖片。

開啟 UserCircle 元件,位於 app/components/user-circle.tsx 中,並進行以下變更,將頭像的背景圖片設定為個人資料圖片 (如果有的話)

如果您現在為幾位使用者提供個人資料圖片,您應該會在整個網站上看到這些圖片顯示出來!

新增刪除帳戶功能

您的個人資料設定模組需要的最後一項功能是刪除帳戶的功能。

刪除資料,尤其是在無 schema 資料庫中,可能會產生「孤兒文件」,或曾經與父文件相關聯,但其父文件在某個時間點被刪除的文件。

您將在本節中採取安全措施來防止這種情況發生。

新增刪除按鈕

您將以與處理登入和註冊表單類似的方式處理此表單。此表單將傳送一個 _action 金鑰,讓 action 函數知道它接收到哪種請求。

app/routes/home/profile.tsx 中,對 ProfileSettings 函數中傳回的 form 進行以下變更

現在,根據點擊的按鈕,您可以在 action 函數中處理不同的 _action

更新 action 函數以使用 switch 陳述式來執行不同的動作

現在,如果使用者儲存表單,將會點擊 'save' case,並且會發生現有的功能。'delete' case 目前沒有執行任何操作。

app/utils/user.server.ts 中新增一個函數,該函數接收 id 並刪除與其關聯的使用者

您現在可以填寫個人資料頁面上 "delete" case 的其餘部分。

您的使用者現在可以刪除他們的帳戶了!

更新資料模型以新增參考完整性

此刪除使用者功能的唯一問題是,當使用者被刪除時,他們所有創作的讚譽都會變成孤兒

您可以使用參考動作來觸發刪除任何讚譽,當其作者被刪除時。

執行 npx prisma db push 以傳播這些變更並產生 Prisma Client。

現在,如果您刪除一個帳戶,該帳戶創作的任何 Kudos 都將隨之刪除!

新增表單驗證

您即將接近尾聲!最後一部分是在個人資料設定表單中連結錯誤訊息處理。

您的 action 函數已經傳回所有正確的錯誤訊息;它們只需要被處理。

app/routes/home/profile.tsx 中進行以下變更以處理這些錯誤

上面的程式碼片段中進行了以下變更

  1. 使用 useActionData hook 來檢索錯誤訊息。這些訊息儲存在 state 變數中,並用於在使用者在提交錯誤表單後返回模組的情況下填寫表單。
  2. 新增了一個錯誤輸出以顯示任何表單級錯誤。
  3. 錯誤資料已傳遞到 FormField 元件,以允許它們在需要時顯示其欄位級錯誤。

進行上述變更後,您將看到任何表單和驗證錯誤都顯示在表單上。

總結 & 接下來的步驟

根據本文所做的變更,您已成功完成 Kudos 應用程式的開發!網站的所有部分都已可運作,並準備好交付給您的使用者。

在本節中,您學習了關於

  • Remix 中的巢狀路由
  • AWS S3
  • 使用 Prisma 和 MongoDB 的參考動作與完整性

在本系列的下一節中,您將總結所建置的應用程式,並將其部署到 Vercel!

請勿錯過下一篇文章!

訂閱 Prisma 電子報