MongoDB、Prisma、Remixを使用してフルスタックアプリケーションをゼロから構築する方法を学ぶこのシリーズの第4回記事へようこそ!今回は、画像アップロードコンポーネントを含むアプリケーションのプロファイル設定セクションを構築し、データに参照整合性を持たせるようにスキーマを設定します。
目次
- はじめに
- プロファイル設定モーダルを構築する
- 画像アップロードコンポーネントを追加する
- プロフィール画像を表示する
- アカウント削除機能を追加する
- フォームのバリデーションを追加する
- まとめと今後の展望
はじめに
このシリーズの前のパートでは、称賛フィード、ユーザーリスト、最近の称賛リスト、称賛送信フォームなど、このアプリケーションの主要な領域を構築しました。
今回のパートでは、ユーザーが自分のプロフィール情報を更新し、プロフィール写真をアップロードする方法を構築することで、このアプリケーションの開発を締めくくります。また、データベースに参照整合性を持たせるために、スキーマにいくつかの変更を加えます。
注:このプロジェクトの開始点は、GitHubリポジトリのpart-3ブランチにあります。このパートの最終結果を確認したい場合は、part-4ブランチに移動してください。
開発環境
提供されている例に従うには、以下が必要です...
- ... Node.jsがインストールされていること。
- ... Gitがインストールされていること。
- ... TailwindCSS VSCode拡張機能がインストールされていること(オプション)。
- ... Prisma VSCode拡張機能がインストールされていること(オプション)。
注:オプションの拡張機能は、TailwindとPrismaに非常に優れたインテリセンスと構文の強調表示を追加します。
プロファイル設定モーダルを構築する
アプリケーションのプロファイル設定ページは、ページの右上にあるプロファイル設定ボタンをクリックするとアクセスできるモーダルに表示されます。
app/components/search-bar.tsx
内
- Prismaによって生成された
Profile
型であるprofile
という名前の新しいpropをエクスポートされたコンポーネントに追加します。 UserCircle
コンポーネントをインポートします。form
の内容の最後にUserCircle
コンポーネントをレンダリングし、新しいprofile
propデータを渡します。これはプロファイル設定ボタンとして機能します。
開発サーバーがすでに実行されている場合、SearchBar
コンポーネントがプロファイルデータを期待するようになったため、ホームページでエラーが発生します。
app/routes/home.tsx
ファイルで、このシリーズのパート2でapp/utils/auth.server.ts
から記述されたgetUser
関数を使用します。この関数を使用して、loader
関数内でログインしているユーザーのデータをロードします。次に、そのデータをSearchBar
コンポーネントに提供します。
SearchBar
は、必要なprofile
データにアクセスできるようになります。以前にこのデータがないためにエラーが発生していた場合は、ブラウザのページをリフレッシュすると、ページの右上隅にプロファイルボタンが正常にレンダリングされていることが表示されるはずです。
モーダルを作成する
目標は、プロファイル設定ボタンをクリックしたときにプロファイル設定モーダルを開くことです。このシリーズの前のセクションで構築した称賛モーダルと同様に、新しいモーダルをレンダリングするネストされたルートを設定する必要があります。
app/routes/home
で、profile.tsx
という名前の新しいファイルを作成し、開始するための次の内容を追加します
上記のコードスニペットは...
- ... 新しい
ProfileSettings
コンポーネントにモーダルをレンダリングします。 - ...
loader
関数内でログインしているユーザーのデータを取得して返します。 - ...
useLoaderData
フックを使用して、loader
関数から返されたuser
データにアクセスします。
この新しいモーダルを開くには、app/components/search-bar.tsx
で、UserCircle
コンポーネントにonClick
ハンドラーを追加し、RemixのuseNavigate
フックを使用してユーザーを/home/profile
サブルートに移動させます。
プロファイル設定ボタンをクリックすると、新しいモーダルが画面に表示されるはずです。
フォームを構築する
構築するフォームには、ユーザーが自分のプロフィール詳細を変更できる3つのフィールドがあります。名前、苗字、部署です。
最初と最後の名前の入力を追加して、フォームの構築を開始します
上記に追加されたものの概要は次のとおりです
- 行われた変更に必要なインポートを追加しました。
- フォームの値を保持する状態の
formData
オブジェクトを作成しました。これにより、これらの値はログインしているユーザーの既存のプロファイルデータにデフォルト設定されます。 - HTMLの
change
イベントとフィールド名をパラメータとして受け取る関数を作成しました。これらは、コンポーネントの入力フィールドの値が変更されると、formData
状態を更新するために使用されます。 - フォームの基本的なレイアウトと2つの入力フィールドをレンダリングします。
現時点では、エラー処理は実装されておらず、フォームは何も実行しません。これらのピースを追加する前に、部署のドロップダウンを追加する必要があります。
app/utils/constants.ts
で、Prismaスキーマで定義された可能なオプションを保持する新しいdepartments
定数を追加します。そのファイルに次のエクスポートを追加します
departments
をapp/routes/home/profile.tsx
ファイルにSelectBox
コンポーネントとともにインポートし、それらを使用してフォームに新しい入力を追加します
この時点で、フォームは正しい入力とそのオプションをレンダリングする必要があります。それらの値は、ログインしているユーザーのプロファイルに関連付けられている現在の値にデフォルト設定されます。
ユーザーがフォームを送信できるようにする
次に構築するピースは、このフォームを機能させるaction
関数です。
app/routes/home/profile.tsx
で、request
オブジェクトからフォームデータを取得し、firstName
、lastName
、department
フィールドを検証するaction
関数を追加します
上記のaction
関数は、次のことを行います
request
オブジェクトから必要なフォームデータポイントを取り出します。- 気にするデータの各ピースが
string
データ型であることを確認します。 - 以前に記述した
validateName
関数を使用してデータを検証します。 - 設定モーダルを閉じて、
/home
ルートにリダイレクトします。
上記のコードスニペットは、さまざまな検証が失敗した場合に関連するエラーもスローします。検証済みのデータを活用するには、ユーザーを更新できる関数を作成します。
app/utils/user.server.ts
で、次の関数をエクスポートします
この関数を使用すると、任意のprofile
データを渡して、id
が指定されたuserId
と一致するユーザーを更新できます。
app/routes/home/profile.tsx
ファイルに戻り、その新しい関数をインポートし、action
関数内でログインしているユーザーを更新するために使用します
ユーザーが保存ボタンを押すと、更新されたプロファイルデータが保存され、モーダルが閉じられます。
画像アップロードコンポーネントを追加する
AWSアカウントを設定する
ユーザーはプロファイルの主要な情報を更新できるようになりましたが、追加すると便利なことの1つは、ユーザーがプロフィール写真を設定できるようにして、他のユーザーがより簡単に識別できるようにすることです。
これを行うには、アップロードされた画像を保持するためのAWS S3ファイルストレージバケットを設定します。AWSアカウントをまだお持ちでない場合は、こちらからサインアップできます。
注:Amazonは、S3への無料アクセスを提供する無料利用枠を提供しています。
IAMユーザーを作成する
アカウントを取得したら、Identity Access Management(IAM)ユーザーをAWSで設定する必要があります。これにより、S3と対話するために必要なアクセスキーIDとシークレットキーを生成できます。
注:IAMユーザーとそのキーを既にお持ちの場合は、先に進んでください。
AWSコンソールホームページに移動します。ページの右上隅にあるユーザー名でラベル付けされたドロップダウンをクリックし、セキュリティ認証情報を選択します。
そのセクションに入ったら、左側のメニューのアクセス管理の下にあるユーザーオプションをクリックします。
このページで、ページの右上にあるユーザーを追加ボタンをクリックします。
これにより、ユーザーを構成できる短いウィザードが表示されます。以下の手順に従ってください
最初のセクションでは、次を求められます
- ユーザー名:任意のユーザー名を指定します。
- AWSアクセスタイプを選択:アクセスキーIDとシークレットキーの生成を有効にするアクセスキー - プログラムによるアクセスオプションを選択します。
ウィザードの2番目のステップで、次の選択を行います
- 「既存のポリシーを直接アタッチする」オプションを選択します。
- 「S3」という用語を検索します。
- AmazonS3FullAccessというラベルの付いたオプションの横にあるチェックマークをオンにします。
- フォームの下部にある[次へ]をクリックします。
アカウントのユーザーの管理と整理を容易にするためにユーザーにタグを追加する場合は、ウィザードの3番目のステップでここに追加します。このページで完了したら、次へをクリックします。
このページの概要が問題なければ、ページの下部にあるユーザーを作成ボタンをクリックします。
そのボタンをクリックすると、アクセスキーIDとシークレットキーが表示されたページに移動します。これらはすぐに使用するため、コピーして簡単にアクセスできる場所に保存してください。
S3バケットを設定する
ユーザーとアクセスキーができたので、AWS S3ダッシュボードに移動し、ファイルストレージバケットを設定します。
このページの右上にあるバケットを作成ボタンをクリックします。
バケットの名前とリージョンを求められます。これらの詳細を入力し、以前に保存したアクセスキーIDとシークレットキーを使用して選択した値を保存します。これらも後で必要になります。
入力したら、フォームの一番下にあるバケットを作成をクリックします。
バケットの作成が完了すると、オブジェクトタブのバケットのダッシュボードページに送信されます。アクセス許可タブに移動します。
このタブで、パブリックアクセスをブロックセクションの下にある編集ボタンをクリックします。このフォームで、すべてのパブリックアクセスをブロックボックスをオフにし、変更を保存をクリックします。これにより、バケットがパブリックとして設定され、アプリケーションが画像にアクセスできるようになります。
そのセクションの下に、バケットポリシーセクションが表示されます。次のポリシーを貼り付け、必ず<bucket-name>
をバケットの名前に置き換えてください。このポリシーにより、画像が公開読み取り可能になります
AWSユーザーとS3バケットが設定されました。次に、キーとバケット構成を.env
ファイルに保存して、後で使用できるようにする必要があります。
Prismaスキーマを更新する
アップロードされた画像へのリンクを保存するフィールドをデータベースに作成します。これらはProfile
埋め込みドキュメントとともに保存する必要があります。したがって、Profile
型ブロックに新しいフィールドを追加します。
Prisma Clientをこれらの変更で更新するには、npx prisma generate
を実行します。
画像アップロードコンポーネントを構築する
app/components
にimage-uploader.tsx
という名前の新しいファイルを作成し、次の内容を追加します
上記のコードスニペットは、完全な画像アップロードコンポーネントです。以下は、何が起こっているかの概要です
preventDefault
関数は、コンポーネントのファイル入力への変更を処理するために定義されています。handleDrop
関数は、コンポーネントのファイル入力でのdrop
イベントを処理するために定義されています。handleChange
関数は、コンポーネントのファイル入力でのchange
イベントを処理するために定義されています。div
は、さまざまなイベントハンドラーが定義されてレンダリングされ、ファイルのドロップ、ドラッグイベント、クリックに反応できます。これらは、画像アップロードをトリガーし、要素がドラッグイベントを受信している場合にのみ表示されるスタイルの変更に使用されます。
このコンポーネントのinput
の値が変更されるたびに、props
のonChange
関数が呼び出され、ファイルデータが渡されます。そのデータがS3にアップロードされるものです。
次に、画像アップロードを処理するサービスを作成します。
画像アップロードサービスを構築する
画像アップロードサービスを構築するには、2つの新しいnpmパッケージが必要です
画像アップロードサービスは、新しいユーティリティファイルに配置されます。app/utils
にs3.server.ts
という名前のファイルを作成します。
アップロードを処理するために、Remixのunstable_parseMultipartFormData
関数を使用します。この関数は、request
オブジェクトのmultipart/form-data
値を処理します。
注:
multipart/form-data
は、フォーム全体にファイルを投稿する場合のフォームデータタイプです。
unstable_parseMultipartFormData
は、2つのパラメータを受け取ります
- フォーム送信から取得された
request
オブジェクト。 uploadHandler
関数。これは、ファイルデータをストリーミングし、アップロードを処理します。
注:
unstable_parseMultipartFormData
関数は、以前に使用したRemixのrequest.formData
関数と同様の方法で使用されます。
作成した新しいファイルに、次の関数とインポートを追加します
このコードは、バケットと対話できるようにS3 APIを設定します。また、uploadHandler
関数も追加します。この関数は
- AWSユーザーとS3バケットを設定するときに保存した環境変数を使用して、S3 SDKを設定します。
- データキーの名前が
'profile-pic'
である限り、request
からファイルデータをストリーミングします。 - ファイルをS3にアップロードします。
- S3が返す
Location
データを返します。これには、S3の新しいファイルのURLロケーションが含まれます。
uploadHandler
が完了したので、request
オブジェクトを実際に入力として受け取り、uploadHandler
とともにunstable_parseMultipartFormData
関数に渡す別の関数を追加します。
この関数にはrequest
オブジェクトが渡されます。これは、後でaction
関数から送信されます。
ファイルデータはuploadHandler
関数を介して渡されます。この関数は、S3へのアップロードを処理し、formData
は、新しいファイルのロケーションをフォームデータオブジェクト内で返します。'profile-pic'
URLは、そのオブジェクトからプルされ、関数によって返されます。
コンポーネントとサービスを活用する
これで、プロファイル写真のアップロードを機能させるために必要な2つのピースが完成しました。それらをまとめます。
アップロードフォームデータを処理するリソースルートを、app/routes
にavatar.ts
という名前の新しいファイルを作成し、次のaction
関数を追加して作成します
上記の関数は、アップロードフォームを処理するために次の手順を実行します
- リクエストユーザーの
id
を取得します。 - リクエストデータで渡されたファイルをアップロードします。
- リクエストユーザーのプロファイルデータを新しい
profilePicture
URLで更新します。 imageUrl
変数でPOST
リクエストに応答します。
これで、ImageUploader
コンポーネントを使用してファイルアップロードを処理し、ファイルデータをこの新しい/avatar
ルートに送信できます。
app/routes/home/profile.tsx
で、ImageUploader
コンポーネントをインポートし、入力フィールドの左側のフォームに追加します。
また、ImageUploader
コンポーネントによって発行されたonChange
イベントを処理する新しい関数と、プロファイル写真データを保存するためのformData
変数に新しいフィールドを追加します。
フォームに移動してファイルをアップロードしようとすると、データはS3、データベース、およびフォームの状態に正しく保存されるはずです。
プロフィール画像を表示する
素晴らしい!画像アップロードはスムーズに動作しています。ユーザーのサークルが表示されるサイト全体にこれらの画像を表示するだけです。
app/components/user-circle.tsx
のUserCircle
コンポーネントを開き、これらの変更を行って、サークルの背景画像をプロフィール写真が利用可能な場合はプロフィール写真になるように設定します
これで、数人のユーザーにプロフィール写真を提供すると、サイト全体に表示されるはずです!
アカウント削除機能を追加する
プロファイル設定モーダルに必要な最後の機能は、アカウントを削除する機能です。
特にスキーマレスデータベースでデータを削除すると、「孤立ドキュメント」、つまり、かつて親ドキュメントに関連付けられていたが、親が途中で削除されたドキュメントが作成される可能性があります。
このセクションでは、そのシナリオに対するセーフガードを導入します。
削除ボタンを追加する
このフォームは、サインインフォームとサインアップフォームの処理方法と同様の方法で処理します。この1つのフォームは、action
関数に受信するリクエストの種類を知らせる_action
キーを送信します。
app/routes/home/profile.tsx
で、ProfileSettings
関数で返されたform
に次の変更を加えます
これで、クリックされたボタンに応じて、action
関数で異なる_action
を処理できます。
action
関数を更新して、switch
ステートメントを使用してさまざまなアクションを実行するようにします
ユーザーがフォームを保存すると、'save'
ケースがヒットし、既存の機能が発生します。'delete'
ケースは現在何も実行しません。
app/utils/user.server.ts
に、id
を受け取り、それに関連付けられたユーザーを削除する新しい関数を追加します
これで、プロファイルページの"delete"
ケースの残りの部分を埋めることができます。
ユーザーはアカウントを削除できるようになりました!
データモデルを更新して参照整合性を追加する
このユーザー削除機能の唯一の問題は、ユーザーが削除されると、そのユーザーが作成したすべての称賛が孤立することです。
参照アクションを使用して、作成者が削除されたときに称賛の削除をトリガーできます。
npx prisma db push
を実行して、これらの変更を伝播し、Prisma Clientを生成します。
これで、アカウントを削除すると、そのアカウントによって作成されたKudos
もアカウントとともに削除されます!
フォームのバリデーションを追加する
終わりに近づいてきました!最後のピースは、プロファイル設定フォームでエラーメッセージ処理をフックアップすることです。
action
関数はすでにすべての正しいエラーメッセージを返しています。それらを処理するだけで済みます。
app/routes/home/profile.tsx
で次の変更を行って、これらのエラーを処理します
上記のコードスニペットでは、次の変更が行われました
useActionData
フックを使用して、エラーメッセージを取得しました。これらは状態変数に保存され、ユーザーが不正なフォームを送信した後にモーダルに戻された場合にフォームに入力するために使用されました。- フォームレベルのエラーを表示するために、エラー出力が追加されました。
- エラーデータは、必要に応じてフィールドレベルのエラーを表示できるように、
FormField
コンポーネントに渡されました。
上記の変更を加えると、フォームと検証のエラーがフォームに表示されます。
まとめと今後の展望
この記事で行われた変更により、称賛アプリケーションを無事に完成させました!サイトのすべてのピースが機能し、ユーザーに出荷する準備ができています。
このセクションでは、以下について学びました
- Remixのネストされたルート
- AWS S3
- PrismaとMongoDBを使用した参照アクションと整合性
このシリーズの次のセクションでは、構築したアプリケーションを取得してVercelにデプロイすることで、物事をまとめます!
次回の投稿をお見逃しなく!
Prismaニュースレターにサインアップ