バリデーションでエラーが出た後、前の情報を残したい
バリデーションでエラーが出た後、前の情報を残したい(formオブジェクトパタンを用いた購入機能を実装中)
質問の箇所
フリマアプリ formオブジェクトパタンを用いた購入機能を実装中。
質問の内容
問題に関する情報
class PurchasesController < ApplicationController before_action :authenticate_user!, except: :index def index @item = Item.find(params[:item_id]) @purchase = Purchase.new @address = @purchase.build_address @purchase_address = PurchaseAddress.new # @purchase_address = @item.build_purchase_address end def new @purchase_address = PurchaseAddress.new # @purchase_address = @item.build_purchase_address end def create @item = Item.find(params[:item_id]) @purchase_address = PurchaseAddress.new(purchase_params) if @purchase_address.valid? @purchase_address.save redirect_to root_path else render :index end # item = Item.find(params[:item_id]) # @purchase = item.create_purchase!(user_id: current_user.id) # @purchase.create_address!(address_params) # redirect_to root_path end private # def purchase_params # params.merge(user_id: current_user.id, item_id: params[:item_id]) # end # def address_params # params.permit(:postal_code, :prefecture_id, :city, :house_number, :phone_number, :building_name).merge(purchase_id: @purchase.id) # end def purchase_params params.permit(:postal_code, :prefecture_id, :city, :house_number, :phone_number, :building_name).merge(user_id: current_user.id, item_id: params[:item_id]) end end
<%= render "shared/second-header"%> <div class='transaction-contents'> <div class='transaction-main'> <h1 class='transaction-title-text'> 購入内容の確認 </h1> <%# 購入内容の表示 %> <div class='buy-item-info'> <%= image_tag @item.image, class: 'buy-item-img' %> <div class='buy-item-right-content'> <h2 class='buy-item-text'> <%= @item.name %> </h2> <div class='buy-item-price'> <p class='item-price-text'>¥<%= @item.price %></p> <p class='item-price-sub-text'><%= @item.shipping.shipping_id %></p> </div> </div> </div> <%# /購入内容の表示 %> <%# 支払額の表示 %> <div class='item-payment'> <h1 class='item-payment-title'> 支払金額 </h1> <p class='item-payment-price'> ¥<%= @item.price %> </p> </div> <%# /支払額の表示 %> <%= form_with url: item_purchases_path, method: [@purchase_address,@purchase,@address], id: 'charge-form', class: 'transaction-form-wrap', local: true do |f| %> <%= render 'shared/error_messages', model: @purchase_address %> <div class='shipping-address-form'> <h1 class='info-input-haedline'> 配送先入力 </h1> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">郵便番号</label> <span class="indispensable">必須</span> </div> <%= f.text_field :postal_code, class:"input-default", id:"postal-code", placeholder:"例)123-4567", maxlength:"8" %> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">都道府県</label> <span class="indispensable">必須</span> </div> <%= f.collection_select(:prefecture_id, Prefecture.all, :id, :prefecture_id, {}, {class:"select-box", id:"prefecture"}) %> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">市区町村</label> <span class="indispensable">必須</span> </div> <%= f.text_field :city, class:"input-default", id:"city", placeholder:"例)横浜市緑区"%> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">番地</label> <span class="indispensable">必須</span> </div> <%= f.text_field :house_number, class:"input-default", id:"addresses", placeholder:"例)青山1-1-1"%> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">建物名</label> <span class="form-any">任意</span> </div> <%= f.text_field :building_name, class:"input-default", id:"building", placeholder:"例)柳ビル103"%> </div> <div class="form-group"> <div class='form-text-wrap'> <label class="form-text">電話番号</label> <span class="indispensable">必須</span> </div> <%= f.text_field :phone_number, class:"input-default", id:"phone-number", placeholder:"例)09012345678",maxlength:"11"%> </div> </div> <%# /配送先の入力 %> <div class='buy-btn'> <%= f.submit "購入" ,class:"buy-red-btn", id:"button" %> </div> <%# <% end %> %> </div> </div> <% end %> <%= render "shared/second-footer"%>
class PurchaseAddress include ActiveModel::Model attr_accessor :user_id, :item_id, :postal_code, :city, :house_number, :phone_number, :building_name, :prefecture_id with_options presence: true do validates :postal_code, format: {with: /\A[0-9]{3}-[0-9]{4}\z/, message: "is invalid. Include hyphen(-)", allow_blank: true } validates :prefecture_id, numericality: {other_than: 1, message: "can't be blank"} validates :city validates :house_number validates :phone_number, format: { with: /\A0[0-9]+\z/, message: 'number is invalid. Include half-width numbers' , allow_blank: true } validates :user_id validates :item_id end def save purchase = Purchase.new address = @purchase.build_address purchase = Purchase.create(user_id: user_id, item_id: item_id) Address.create(postal_code: postal_code, city: city, house_number: house_number, phone_number: phone_number, building_name: building_name, prefecture_id: prefecture_id, purchase_id: purchase.id) end end
問題に関するターミナルやコンソールの情報:
From: /Users/user/projects/furima-35933/app/controllers/purchases_controller.rb:19 PurchasesController#create: 16: def create 17: @item = Item.find(params[:item_id]) 18: @purchase_address = PurchaseAddress.new(purchase_params) => 19: binding.pry 20: if @purchase_address.valid? 21: @purchase_address.save 22: redirect_to root_path 23: else 24: render :index 25: end 26: # item = Item.find(params[:item_id]) 27: # @purchase = item.create_purchase!(user_id: current_user.id) 28: # @purchase.create_address!(address_params) 29: # redirect_to root_path 30: end [1] pry(#<PurchasesController>)> @purchase_address => #<PurchaseAddress:0x00007ffd49fb2450 @building_name="", @city="日高市", @house_number="森山1-5-77", @item_id="31", @phone_number="09084842343", @postal_code="213-3456", @prefecture_id="3", @user_id=33> [2] pry(#<PurchasesController>)> @purchase_address.valid? => true [3] pry(#<PurchasesController>)> exit
解決したいこと
formオブジェクトパタンを用いた購入機能を実装中。 バリデーションでエラーが出た後、前の情報を残したい
調べた内容とそこから立てた仮説
カリキュラムの フォームオブジェクトパタンの定義方法を参考に購入機能を実装してみた コントローラでpurchase_addressのインスタンスを生成し、form_with に代入ができたと思いますが、form_wiht で入力したデータをコントローラで送ってきた値をバリデーションをかけ、入力問題がある時、エラーメッセージが出ていたが、入力した内容は消え、form_wihtにはコントローラで生成したpurchase_addressのインスタンスを指定したにも関わらず、残せないのは、今回の問題点ではと考えております、そのため、form_withに新たにpurchaseコントローラでインスタンス@purchase,@address を生成し、指定したが、解決できなかった、なので問題は別のところにあるのではと考えておりますが.今回においては、上記の@purchaseと@addressは、form_withの実装で特別使用することがないインスタンス変数になる め,@purchase_addressのみを使用して実装ができる記述で進めていきますので、今回は不要となります。
はい、ダメでした!
- 検証
- 「@purchase_address」というインスタンス変数に正しく値が入っているかを確認するところ
[1] pry(#<PurchasesController>)> @purchase_address => #<PurchaseAddress:0x00007ffd49fb2450 @building_name="", @city="日高市", @house_number="森山1-5-77", @item_id="31", @phone_number="09084842343", @postal_code="213-3456", @prefecture_id="3", @user_id=33> [2] pry(#<PurchasesController>)> @purchase_address.valid? => true
- ちゃんと入ってることがわかりましたが、、、 入力したデータを購入ページに反映させたい場合は、form_with内にある「@purchase_address」に含まれる値を受け取り、ビューに既存のデータとして反映させます。つまり、form_with の記述は問題があるかを確認する必要あるここでもう一回form_with 見直します。
<%= form_with method: @purchase_address, ←(キーが「method:」になっています)
それでform_withの記述を修正:
<%= form_with url: item_purchases_path, model: @purchase_address, id: 'charge-form', class: 'transaction-form-wrap', local: true do |f| %>
- そして、以下の手順にしたがって、
①rails sのし直し。 余計なエラーが出ることを防ぐため、一度最新の状態にリセットさせます。
②localhost: 3000のトップページから購入ページに移る。 こちらも上記の理由と同じです。確実に最新の状態でページを始められる状態にします。
③購入ページで「購入ボタン」を押した後に、入力情報が残るかどうかを確認。
ターミナル上:
From: /Users/user/projects/furima-35933/app/controllers/purchases_controller.rb:15 PurchasesController#create: 12: def create 13: @item = Item.find(params[:item_id]) 14: @purchase_address = PurchaseAddress.new(purchase_params) => 15: binding.pry 16: if @purchase_address.valid? 17: @purchase_address.save 18: redirect_to root_path 19: else 20: render :index 21: end 22: # item = Item.find(params[:item_id]) 23: # @purchase = item.create_purchase!(user_id: current_user.id) 24: # @purchase.create_address!(address_params) 25: # redirect_to root_path 26: end [1] pry(#<PurchasesController>)> @purchase_address => #<PurchaseAddress:0x00007ff483a4c750 @item_id="31", @user_id=33> [2] pry(#<PurchasesController>)> @purchase_address => #<PurchaseAddress:0x00007ff483a4c750 @item_id="31", @user_id=33> [3] pry(#<PurchasesController>)> @purchase_address.valid? => false [4] pry(#<PurchasesController>)> @purchase_address.errors.full_messages => ["Postal code can't be blank", "Prefecture can't be blank", "Prefecture can't be blank", "City can't be blank", "House number can't be blank", "Phone number can't be blank"] [5] pry(#<PurchasesController>)> exit
また、新たな問題発見!!!!
- どうやら、インスタンス @purchase_addressに@item_id="31", @user_id=33 の情報しかない、なぜ?ストロングパラメータに問題でも?
def purchase_params params.permit(:postal_code, :prefecture_id, :city, :house_number, :phone_number, :building_name).merge(user_id: current_user.id, item_id: params[:item_id])
permit(:postal_code, :prefecture_id, :city, :house_number, :phone_number, :building_name)
の情報が受け取らなかった!!!ていうことは、、、指定しなかったのでは、、、
def purchase_params params.require(:purchase_address).permit(:postal_code, :prefecture_id, :city, :house_number, :phone_number, :building_name).merge(user_id: current_user.id, item_id: params[:item_id]) end
require(:purchase_address)追記し、もう一回試したら、、、、
したら、無事解決できました!!!
まとめ:from_withの記述が間違ったことは今回の原因でした!!!とても初歩的なミス!!!もう二度と同じ間違いを犯さないようにメモとして残しときます。
最後:問題解決に手伝って頂いたメンターの方に感謝!!
(テックキャンプ 102期 受講生)
追記:クレジットカード情報の項目は省いております。