出品機能実装で役立ったfields_for

初めましてみほうです。

ただいまプログラミングスクールでメルカリのコピーサイトを作成しているところです。

私はA班に所属し、スクラムマスターをしています。

別ブログでこれまで記事を書いてきましたが、今回書くにあたって書きづらいところがあったためQiitaで投稿することにしました。

今回は本日までの出品機能実装で新しく学んだfields_forについて書きます。


追記 

ancestryについての記事を作成しました


環境

Rails 5.2.3


出品機能で学んだこと

出品機能では入力した値をデータベースに保存する必要があります。

メルカリの出品画面で必要な項目をA班では以下のように分けました。

・Itemsテーブル(商品情報保存)

商品名・商品の説明・サイズ・商品の状態・seller_id(出品したuserのid)・buyer_id(購入したuserのid)・brand_id・shipment_id・価格

seller_id、buyer_idについてはリンク先の記事参照

・Brandsテーブル(ブランド情報保存)

ブランド名

・Shipmentsテーブル(配送情報保存)

配送料の負担・配送の方法・発送元の地域(prefecture_id)・発送までの日数

・Categoriesテーブル(カテゴリ名保存)

カテゴリー名・ancestry(次の記事で解説予定)

・Items_categoriesテーブル(中間テーブル)

item_id・category_id

また画像についてはActive_Storageを利用して保存しています。

以上のテーブルに値を保存するのにfields_forが役立ちました。


fields_forによる別モデルへの保存

 先ほど挙げたテーブルに値を保存するためには出品画面を作るビューに別モデルの値が保存できるよう指示を出す必要があります。

 そのために使うのがfields_for

 fields_forはアソシエーション等で紐づいているモデルを1つのフォームで入力させて登録・更新する方法です。

この記事が参考になりました。

 使うためにはモデル・コントローラーである記述をする必要があります。


Modelの記述

ItemテーブルのモデルであるItem.rbを例にとって説明します。

Item.rbにおいてfields_forを使うために以下の記述をしました。

(app/models/item.rb)

accepts_nested_attributes_for :shipment
accepts_nested_attributes_for :brand
has_many :items_categories
has_many :categories, through: :items_categories

 ポイントはaccepts_nested_attributes_forで指定するところです。

この記述をすることでshipment、brand、items_categoryテーブルと関連づけることができます。

items_categoryについてはhas_manyの関係であるため複数形で書き、shipmentとbrandはそれぞれhas_one、belongs_toの関係であるため単数形で書いています。


Controllerの記述

Controllerでもfields_forを使うために指定する必要があります。

itemを作成するnewアクションでは以下のように書いています。


(app/controllers/items_controller.rb)
def new
@item = Item.new
@parents = Category.all.order("id ASC").limit(13)
@item.build_shipment
@item.build_brand
end

ポイントはbuildです。この記述をすることでnewアクション内でfields_forの記述をすることができます。

has_oneやbelongs_toではbuildが使えないためbuild_モデル名としています。

またストロングパラメーター内でモデル名_attributesと指定する必要もあります。

permitの中で指定をし、createアクションでメソッドを持って来ればインプットした値を保存することができます。


(app/controllers/items_controller.rb)

def create
 @item = Item.new(item_params)
 if @item.save!
shipment_id = Shipment.find(@item.id).id #Shipmentテーブルのidを取り出す
item = Item.find(@item.id) #作成したItemのidを取り出す
item.update(shipment_id: shipment_id)  #Itemテーブルにshipment_idのカラムを入れる
redirect_to root_path
 else
redirect_to new_item_path
 end
end

def item_params
params.require(:item).permit(:name, :size, :description, :price, :item_status,
 shipment_attributes: [:id, :cost_payer, :method, :days, :prefecture_id],images: [],
 brand_attributes: [:id, :name], category_ids: []).merge(seller_id: current_user.id)
end


viewの記述

記述量が少ないbrandモデルで説明します。

fields_forを使うと以下の記述になります。


(app/views/items/new.html.haml)
.form-group.brand--container
= f.fields_for :brand do |brand|
= brand.label "ブランド"
%span.form-require-gray 任意
%div
= brand.text_field :name, placeholder: "例)シャネル", value: "", class: "input-default"

fields_for :brandでbrandモデルを指定してtext_field内でnameカラムを指定することでbrandテーブル内にnameの値を保存することができます。

実際の画面だとこのようになります。

image.png

ここでブランドのnameを入れることでデータベースに保存できるようになります。


あとがき

チーム開発において2回目のスプリントレビューが終わり、

今週金曜日にある3回目のスプリントレビューまでに必須項目全て実装を目標にチーム開発しています。

6/3〜6/9までの間、全員が揃った日は3日のみでした。

それでも進捗具合が良く、いいメンバーに恵まれたと思います。

もし本記事の内容で誤っている箇所などございましたら、ご教授頂けますと幸いです。

何卒よろしくお願いいたします。