はじめに
こんにちは!上野と申します!
これからRails + Next.jsで連携したデータの渡し方と受け取り方、
ビューに表示するやり方を身につけたので書きます!
実務未経験の人が書いたコードと知識なので鵜呑みにしないでください。。。
私は今レシピの原価計算が計算機なしでできるアプリを開発途中です
その中で学んだ事を書いていきます。
設定としてはAuth0で認証していて関係性は一つの仕入れ先に対して複数の原材料です。
まずは仕入れ先の登録から
postmanでこのようなリクエストボディを送れば登録できます。
{
"supplier": {
"name": "postmanから",
"contact_info": "連絡先"
}
}
レスポンス
{
"supplier": {
"id": 10,
"user_id": "auth0|64b9c1cfb6c808c6f9ccd15e",
"name": "postmanから",
"contact_info": "連絡先",
"created_at": "2023-07-28T08:53:07.481+09:00",
"updated_at": "2023-07-28T08:53:07.481+09:00"
}
}
登録はこれでok!!ですが表示は仕入れ先だけだと足りないのでsupplier#indexで
少しばかりJSONをいじる↓SuppliersController
def index
suppliers = current_user.suppliers.includes(:ingredient).oldest
render json: { suppliers: suppliers.map do |supplier|
supplier.as_json(include: {
ingredeients: {},
})
end }, status: :ok
end
これで現在ログインしているユーザーが作成した仕入れ先と
多の関係である紐づいた原材料を効率良く、古い順で表示させるようsuppliers
変数に代入
そしてfrontにjson形式でレスポンスを返すためにrender
で一覧なのでmapメソッドを使い繰り返し処理をし、as_jsonメソッドでカスタマイズできるjsonにしてから、原材料の情報もjsonで取得している。
↓取得結果
{
"id": 10,
"user_id": "auth0|64b9c1cfb6c808c6f9ccd15e",
"name": "postmanから",
"contact_info": "連絡先",
"created_at": "2023-07-28T08:53:07.481+09:00",
"updated_at": "2023-07-28T08:53:07.481+09:00"
"ingredients": []
}
このようにsupplierからingredientsの空の配列が取得できているのが確認できる
ですがこのように複雑なjsonになってくると,パフォーマンスの関連で
不要なカラムは削ぎ落としたくなるのです
表示させるつもりのない、作成日と更新日を削ぎ落としましょう
def index
suppliers = current_user.suppliers.oldest.select(:id, :user_id, :name, :contact_info).includes(:ingredients).map do |supplier|
supplier.attributes.merge(
ingredients: supplier.ingredients.select(:id, :supplier_id, :buy_cost, :buy_quantity, :unit, :name)
)
render json: { suppliers:}, status: :ok
# ↑ショートカット記法で{ suppliers: suppliers }同じだったら{ suppliers:}このように省略できる。
end
これで先ほどと少々記述が複雑になりましたがcreated_atとupdated_atが削ぎ落とされました。
なぜsuppliers変数の段階で加工したかというとrender
から見た目をスッキリしたいのと後々モデルに移動しやすいからです。(最初の量だったらコントローラに記述でいいと思います)
selectメソッドを使うとDBから取得する前に、
ActiveRecordが不要なカラムを削ぎ落としてくれるのでこのアプローチにしました。
次にモデルにDB操作責務を渡します
理由はコントローラは、ビューからリクエストをもらい、上記のように、複雑な加工をしてから、
DBにお願いします。そして、DBから情報をもらい、ビューにレスポンスを返しと中間地点の役割を果たしています。
そこで、複雑な加工とDBにお願いと受け取る役割をモデルに渡すことによって、
コントローラはスッキリし、リクエストとレスポンスに集中できるというわけです!
Fat Model, Skinny Controllerの原則を意識しています
def self.with_ingredients_for_user(user)
user.suppliers.oldest.select(:id, :user_id, :name, :contact_info).includes(:ingredients).map do |supplier|
supplier.attributes.merge(
ingredients: supplier.ingredients.select(:id, :supplier_id, :buy_cost, :buy_quantity, :unit, :name)
)
end
end
引数にuserを持たせているのは、before_actionはアクションメソッド(コントローラ)が行われる前に行うことができるので、current_userはコントローラ内でしか使えないのがわかります。
なので引数にここはなんでもいいですが、わかりやすくuserです。
そして大事なのはクラスメソッドにしているところです。あとは、中身はそのまんまですね。
そしてコントローラに渡すとき↓
def index
suppliers = Supplier.with_ingredients_for_user(current_user)
render json: { suppliers: }, status: :ok
end
(current_user)を渡してあげればいいのです!
めちゃくちゃスッキリしましたね!!
結果: suppliers#indexのAPIを叩くとレスポンスはこうなる
{
"id": 10,
"user_id": "auth0|64b9c1cfb6c808c6f9ccd15e",
"name": "postmanから",
"contact_info": "連絡先",
"ingredients": []
}
はい!created_atとupdated_atがなくなりました!ちなみに表示させたい時はもちろんですが取得は必須です
今回の場合表示させる機会がないため削ぎ落としただけです。
次は原材料の作成です
リクエストボディはこんな感じです
{
"ingredient": {
"name": "サンプル原材料",
"buy_cost": 1000,
"buy_quantity": 500,
"unit": "g",
"supplier_id": 10
}
}
レスポンスボディはこちら
"ingredient": {
{
"id": 24,
"supplier_id": 10,
"buy_cost": "1000.0",
"buy_quantity": "500.0",
"unit": "g",
"name": "サンプル原材料",
"created_at": "2023-07-28T10:15:57.022+09:00",
"updated_at": "2023-07-28T10:15:57.022+09:00"
}
}
因みにアクティブレコードのログではこれ
Parameters: {"ingredient"=>{"name"=>"サンプル原材料", "buy_cost"=>1000, "buy_quantity"=>500, "unit"=>"g", "supplier_id"=>10}}
そして先ほど作成した、suppliers#indexアクションのapiを叩くと
{
"id": 10,
"user_id": "auth0|64b9c1cfb6c808c6f9ccd15e",
"name": "postmanから",
"contact_info": "連絡先",
"ingredients": [
{
"id": 24,
"supplier_id": 10,
"buy_cost": "1000.0",
"buy_quantity": "500.0",
"unit": "g",
"name": "サンプル原材料"
},
{
"id": 25,
"supplier_id": 10,
"buy_cost": "550.0",
"buy_quantity": "500.0",
"unit": "g",
"name": "サンプル原材料2"
}
]
}
はい〜!postmanからという名前の仕入れ先の原材料が表示できました〜!!
しっかりと作成日と更新日が削ぎ落とされてます
ビューで見るとこんな感じ、postmanで作成したデータが表示されています。
無事表示に成功しました!!
次にポストマンではなくfront側のフォームから仕入れ先の送信できるようにします!