はじめに
Career(過去の経歴)の機能を実装していきます。
Railsを使ったCRUD(表示・作成・更新・削除)からRailsのエンドポイント、Next.jsでリクエストをして表示させるところまで全て通して実装します。
前回まではなるべく途中の全てのコードを載せるように努めてきていましたが、今回は全てのコードは載せません。githubにコードをあげているので必要に応じて参照してください。
今回はこれまで実装してきたことと同じことを違う形で実装する箇所がほとんどです。
DB設計ができたところでコードの実装はご自身で試してみるとより定着すると思います。
ぜひ挑戦してみてください!
ゴールの確認
データベースの設計とcareerモデルを作成する
railsでcareerのCRUD機能を実装する
railsでcareer一覧を返すエンドポイントを作成する
nextjsでcareer一覧のエンドポイントを実行する
nextjsでcareer一覧を画面に表示させる
ここでの差分はgithubにあげています。参考にしてみてください
テーブル設計
まずは経歴を保存するためのテーブルを作成する必要があります。
title→就職先などやっていたことを記載する
description→そこでやっていたことを簡単にまとめたもの
started_at→いつから始めたか
ended_at→いつ終了したか
user_idをもたせることにしました。userは一人しかいないので、そのユーザーと紐づける必要はないのですが、なんとなく今後の仕様変更でuserが増えてしまったときのためや、リレーションがある場合の方が実装の紹介できる機能が多そうなのでリレーションさせてみました。
それではmigrationファイルとmodelを作成していきます
rails g model Career
マイグレーションファイルはこのようにしました
class CreateCareers < ActiveRecord::Migration[7.0]
def change
create_table :careers do |t|
t.references :user
t.string :title, null: false, default: ""
t.text :description, null: false
t.date :started_at, null: false, default: -> { '(CURRENT_DATE)' }
t.date :ended_at
t.timestamps
end
end
end
started_atは万が一nullになる場合は現在の日付を入れるようにしています。
ended_atは今もなおその経歴の最中のときは定義できないのでnull有りです
マイグレーションを実行(rails db:migrate)しましょう。
RailsでCareerのCRUD処理
テーブルとモデルができたのでCRUD処理を作成していきましょう。
見た目だけを作成したコミットを作成しています。見た目の作成が面倒な場合はこちらをベースに始めてみてください。
(左からindex, new, show)
editはnewとほとんど同じになります。また、partialにてリファクタリングをします。
こだわっているところ
careersメソッドの実装
indexで実装したcareersメソッドは下記のようにしています。
def user
@user ||= User.first
end
def careers
@careers ||= user.careers
end
user.careersのようにuserのリレーションで経歴の一覧を取り出すようにしています。
このように指定することでcareersテーブルの中からuser_idがuserのidと同じものに絞り込んで取得されます。Career.find_by(user_id: user.id)
としても同じことですが、もし私がレビュアーであればこれは修正するように求めます。
ユーザーが複数存在する場合、そのユーザー以外のレコードが表示されるのは大事故です。
Careerモデルから直接呼び出す場合、万が一user_idの指定がうまくできていない場合に他人のレコードが表示されかねません。user.careers
かCareer.find_by(user_id: user.id)
で統一するなら、そのような事故が起きにくいのは前者と考えているからです。
partialの使い方
new.html.erbにて
<%= render partial: "career_form" , locals: { career: career } %>
とformパーシャルにcareerを渡しています。
careerはhelperメソッドなため、localsに指定しなくてもcareerを実行できますが、あえて渡しています。パーシャルの中で必要な変数は明示しておくことで他のメンバーが自分の作成したパーシャルを呼び出すときに必要な変数を把握しやすくする目的があります。
エンドポイントの作成
エンドポイントの作成はこちらのコミットで行っています。
def careers
@careers ||= user.careers.order(started_at: :asc)
end
next.js側では開始日順に上から表示させた方が分かりが良いのでこの段階で並び順を作成しています。
def index
render json: careers, each_serializer: CareerSerializer
end
以前、userでserializerを使ったときは
render json: user, serializer: UserSerializer
と使いましたが、今回はeach_serializserをキーにしています。
jsonで返すものが配列の場合はeach_serializerにして、その値にはその要素をどうシリアライズするかを定義したシリアライザークラスを指定します。
attribute :started_at, key: :startedAt
attribute :ended_at, key: :endedAt
next.jsではスネークケースではなく、キャメルケースで扱いたいので、ここでキー名を変更しています。
Next.jsの実装
こちらもuserを表示させたときとほどんど一緒です。
異なる点は複数のリソースを取得するので、配列をループして要素を画面に表示させていきます。
その際にreactやnextjsではその親domに対してkeyを渡して、それぞれのdomが別物であることがわかるような印をつける必要があります。
import styles from "@/features/careers/components/career-item.module.scss";
import { CareerItemProps } from "@/features/careers/types";
export default function CareerItem(props: CareerItemProps) {
const { career } = props;
return (
<div className={styles.item} key={career.id}>
<div className={styles.icon}></div>
<div className={styles.content}>
<div className={styles.head}>
<div className={styles.title}>{career.title}</div>
<div className={styles.period}>
{career.startedAt} ~ {career.endedAt}
</div>
</div>
<div className={styles.description}>{career.description}</div>
</div>
</div>
);
}
日付のフォーマット
railsが返す日付は”2024/06/01″ですが、
このように日付をYY/MMでシンプルに表示させるために日付をフォーマットしたいです。
日付のフォーマットにはライブラリを使用しました。→ day.js
getterでこのフォーマッタを通して呼び出すような実装にしてみました
get endedAt() {
return this.formatDate(this._endedAt);
}
private formatDate(date?: string) {
if (date) {
return dayjs(date).format("YY/MM");
} else {
return "Now";
}
}
おわりに
これにて、Next.jsのトップページの経歴の一覧が動的になりました。
ここで行った実装はまた別の機能を増やすときのベースになります。
細かいコードの書き方は覚える必要は全くないですが、処理の流れやデータの流れを説明できるくらい把握できていれば、web開発の解像度がグッと高まったと言えると思います。
今回と類似の実装はこのポートフォリオ作成の中でも何度か行う予定です。
完成コードのカンニングなしで実装できなかった方も次は今回よりも自走できるように復習しておきましょう!
コメント