バリデーションでエラーが出た後、前の情報を残したい

バリデーションでエラーが出た後、前の情報を残したい(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期 受講生)

追記:クレジットカード情報の項目は省いております。

自己学習メモ:エラー解決(undefined local variable or method `purchase' for #<PurchaseAddress:0x00007ffd49e9b670>)

自己検証の内容:

エラー画面

 スクリーンショットを貼る

undefined local variable or method `purchase' for #<PurchaseAddress:0x00007ffd49e9b670>

解決したいこと

  • formオブジェクトパタンを用いた購入機能を実装中。
  • form_withで入力したデータはdbに保存したいが、purchaseテーブルへの保存ができたが、address テーブルへの保存ができなかったこと

調べた内容とそこから立てた仮説

 カリキュラムの donation appを参考に購入機能を実装してみた コントローラでpurchase_addressのインスタンスを生成し、form_with に代入ができたと思いますが、form_wiht で入力したデータをコントローラで送ってきた値をバリデーションをかけ、エラーメッセージも出ていて、purchase_address.rbでsaveできないのは今回の問題点かと思いますが、カリキュラムと同じ設定方法で記述したため、問題は別のところにあるのではと考えております。

 

仮説を元に行った作業内容

 (例)コントローラーにbinding.pryを設け処理をとめた。  何が受け取れているのかを確認したところ、トークンの受け取りは確認できなかった。  JSからパラメーターが送られていないのか確認しようとしたが、適切に確認できているのか  分からなかった。

新たにmodelsディレクトリ直下にファイルを作成し、クラスを定義した(formオブジェクトパタンを実行)

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(-)"}
    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' } 
    validates :user_id
    validates :item_id
  end

  def save
    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)

    # 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

end

purchaseコントローラ

class PurchasesController < ApplicationController
  before_action :authenticate_user!, except: :index
  def index
    @item = Item.find(params[:item_id])
    @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)
    # @purchase_address = item.build_purchase_address(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

フォームのmodelオプションを設定したコード:現段階では、pay.jpを導入していないため、カード情報の入力をコメントアウトしている

<%= render "shared/second-header"%>
<%= form_with url: item_purchases_path, method: @purchase_address, local: true do |f| %>
<%= render 'shared/error_messages', model: @purchase_address %>

<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 id: 'charge-form', class: 'transaction-form-wrap',local: true do |f| %> %>
    <%# カード情報の入力 %>
    <%# <div class='credit-card-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 :hoge, class:"input-default", id:"card-number", placeholder:"カード番号(半角英数字)", maxlength:"16" %> %>
        <%# <div class='available-card'> %>
          <%# <%= image_tag 'card-visa.gif', class: 'card-logo' %> %>
          <%# <%= image_tag 'card-mastercard.gif', class: 'card-logo' %> %>
          <%# <%= image_tag 'card-jcb.gif', class: 'card-logo' %> %>
          <%# <%= image_tag 'card-amex.gif', class: 'card-logo' %> %>
        <%# </div> %>
      <%# </div> %>
      <%# <div class="form-group"> %>
        <%# <div class='form-text-wrap'> %>
          <%# <label class="form-text">有効期限</label> %>
          <%# <span class="indispensable">必須</span> %>
        <%# </div> %>
        <%# <div class='input-expiration-date-wrap'> %>
          <%# <%= f.text_area :hoge, class:"input-expiration-date", id:"card-exp-month", placeholder:"例)3" %> %>
          <%# <p>月</p> %>
          <%# <%= f.text_area :hoge, class:"input-expiration-date", id:"card-exp-year", placeholder:"例)23" %> %>
          <%# <p>年</p> %>
        <%# </div> %>
      <%# </div> %>
      <%# <div class="form-group"> %>
        <%# <div class='form-text-wrap'> %>
          <%# <label class="form-text">セキュリティコード</label> %>
          <%# <span class="indispensable">必須</span> %>
        <%# </div> %>
        <%# <%= f.text_field :hoge, class:"input-default", id:"card-cvc", placeholder:"カード背面4桁もしくは3桁の番号", maxlength:"4" %> %>
      <%# </div> %>
    <%# </div> %>
    <%# /カード情報の入力 %>
    
    <%# 配送先の入力 %>
    <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"%>

もう一回 やり直してみたら、やっぱり問題点はここでした!!!

  def save
    purchase = Purchase.create(user_id: user_id, item_id: item_id)   # 変数purchase に代入しなかったため
    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)

無事解決できたのは嬉しいけど、こんなミスをもう二度と経験したくないから、メモとして残しときます!!!

クレジット決済ミニアプリ実装について

その2テストコードを実装

RSpecとFactoryBotのGemを導入

Gemfileに以下を追記しましょう。:development, :testと指定するグループ内に記述すること

# 省略

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end

# 省略

rspec gemを導入

USERnoMacBook-puro:payjp_practice user$ bundle install
# 省略
Using webpacker 4.3.0
Bundle complete! 19 Gemfile dependencies, 80 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
USERnoMacBook-puro:payjp_practice user$

Rspecをインストール

USERnoMacBook-puro:payjp_practice user$ rails g rspec:install
Running via Spring preloader in process 49554
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb
USERnoMacBook-puro:payjp_practice user$ 

.rspecの設定ファイルに、テストコードの実行ログが見やすくなるため

--require spec_helper
--format documentation # ←こちらを記述!

Orderモデルの単体テストコードを記述するspecファイルを生成

USERnoMacBook-puro:payjp_practice user$ rails g rspec:model order
Running via Spring preloader in process 49604
      create  spec/models/order_spec.rb
      invoke  factory_bot
      create    spec/factories/orders.rb
USERnoMacBook-puro:payjp_practice user$ 

FactoryBotでデータを生成

FactoryBot.define do
  factory :order do
    price {3000} #←こちらを記述
  end
end

テストコードを実装

require 'rails_helper'

RSpec.describe Order, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"#←こちらを削除
  before do
    @order = FactoryBot.build(:order) # インスタンスを生成
  end

  context '内容に問題ない場合' do  
    it "priceがあれば保存ができること" do # example1
      expect(@order).to be_valid
    end
  end

  context '内容に問題がある場合' do 
    it "priceが空では保存ができないこと" do # example2
      @order.price = nil
      @order.valid?
      expect(@order.errors.full_messages).to include("Price can't be blank")
    end
  end
end

テスト成功か確認

USERnoMacBook-puro:payjp_practice user$  bundle exec rspec spec/models/order_spec.rb 

Order
  内容に問題ない場合
    priceがあれば保存ができること
  内容に問題がある場合
    priceが空では保存ができないこと

Finished in 0.0213 seconds (files took 1.21 seconds to load)
2 examples, 0 failures # ← 成功した

USERnoMacBook-puro:payjp_practice user$ 

未経験でのWebエンジニア転職を目指して、プログラミングを学習しています。日々の学習した内容をアウトプット用にはてなを書き始めました。TECH CAMP 102 期学生

クレジット決済ミニアプリ実装について

クレジット決済ミニアプリ実装について(その1)

クレジットカード決済代行サービスを利用することは、以下のようなメリットがあります。

  • 会社ごとに事務的手続きをする必要がない
  • 決済代行サービスが提供するAPIなどを用いることで、クレジットカード情報の取り扱いに関するセキュリティ対策をしなくても、安全なやり取りができる

    早速、アプリケーションを作成レツゴ!!!

# アプリを作成するprojectsディレクトリに移動
% cd ~/projects

# payjp_practiceという新規アプリをRailsバージョン6.0で、-dオプションをつけMySQLの使用を明示して作成
% rails _6.0.0_ new payjp_practice -d mysql

# payjp_practiceに移動
% cd payjp_practice

# データベースの生成
% rails db:create

でた!ケーキマーク Image from Gyazo

# 開発用ファイル生成
USERnoMacBook-puro:payjp_practice user$ rails db:create
Created database 'payjp_practice_development'
Created database 'payjp_practice_test'
USERnoMacBook-puro:payjp_practice user$ 

order モデルを作成

# rails g model order
USERnoMacBook-puro:payjp_practice user$ rails g model order
Running via Spring preloader in process 47891
      invoke  active_record
      create    db/migrate/20211003013422_create_orders.rb
      create    app/models/order.rb
      invoke    test_unit
      create      test/models/order_test.rb
      create      test/fixtures/orders.yml
USERnoMacBook-puro:payjp_practice user$ 

ordersテーブルを作成

class CreateOrders < ActiveRecord::Migration[6.0]
  def change
    create_table :orders do |t|

      t.integer :price  ,null: false # ←追加
      t.timestamps
    end
  end
end

コマンドを実行してデータベースにテーブル情報を反映

USERnoMacBook-puro:payjp_practice user$ rails db:migrate
== 20211003013422 CreateOrders: migrating =====================================
-- create_table(:orders)
   -> 0.0396s
== 20211003013422 CreateOrders: migrated (0.0397s) ============================

USERnoMacBook-puro:payjp_practice user$ 

Sequel Pro確認ok! Image from Gyazo

バリデーションを設定

class Order < ApplicationRecord
   validates :price, presence: true
end

ルーティングを設定

Rails.application.routes.draw do
  root to: 'orders#index'
  resources :orders, only:[:create]
end

ordersコントローラーを作成

USERnoMacBook-puro:payjp_practice user$ rails g controller orders
Running via Spring preloader in process 48287
      create  app/controllers/orders_controller.rb
      invoke  erb
      create    app/views/orders
      invoke  test_unit
      create    test/controllers/orders_controller_test.rb
      invoke  helper
      create    app/helpers/orders_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/orders.scss
USERnoMacBook-puro:payjp_practice user$ 

indexアクションとcreateアクションを定義

class OrdersController < ApplicationController
  def index
  end

  def create
  end

end

ビューファイルindex.html.erbを作成,記述

<%= form_with  model: @order, id: 'charge-form', class: 'card-form',local: true do |f| %>
  <div class='form-wrap'>
    <%= f.label :price, "金額" %>
    <%= f.text_field :price, class:"price", placeholder:"例)2000" %>
  </div>
  <%= f.submit "購入", class:"button", id: "button" %>
<% end %>

とstyle.cssファイルを作成,記述略

保存できるようにする

class OrdersController < ApplicationController
  def index
    @order = Order.new
  end

  def create
    @order = Order.new(order_params)
    if @order.valid?
      @order.save
      return redirect_to root_path
    else
      render 'index'
    end
  end

  private

  def order_params
    params.require(:order).permit(:price)
  end

end

ブラウザで挙動を確認

Image from Gyazo

ok!!!しっかりデータ保存できた!

バリデーションがしっかりと機能し、エラーメッセージが出力されるか確認

  • gemファイルにgem 'pry-rails'を記述
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'pry-rails'
USERnoMacBook-puro:payjp_practice user$ bundle install
Using webpacker 4.3.0
中略
Bundle complete! 18 Gemfile dependencies, 78 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
USERnoMacBook-puro:payjp_practice user$ 
  • 次orders_controller.rbにbinding.pryを追記
  def create
    @order = Order.new(order_params)
    binding.pry # ←ここに記述
    if @order.valid?
      @order.save
 binding.pry # ←ここに記述しても機能しない
      return redirect_to root_path
    else
      render 'index'
    end
  end
  • からのデータを送信すると
Started POST "/orders" for ::1 at 2021-10-03 11:33:34 +0900
Processing by OrdersController#create as HTML
  Parameters: {"authenticity_token"=>"K8vy474+YQ1fEO0CXPTV/FfvsLN+DzPIqHqklYMmPqWvj2ujBzwm3XW3dMYNW+Rw7mZQ7gOEbhqUvjYDZFRbcQ==", "order"=>{"price"=>""}, "commit"=>"購入"}

From: /Users/user/projects/payjp_practice/app/controllers/orders_controller.rb:8 OrdersController#create:

     6: def create
     7:   @order = Order.new(order_params)
 =>  8:   binding.pry
     9:   if @order.valid?
    10:     @order.save
    11:     return redirect_to root_path
    12:   else
    13:     render 'index'
    14:   end
    15: end

[1] pry(#<OrdersController>)> 
  • @order.valid?を叩くと
[1] pry(#<OrdersController>)> @order.valid?
=> false
  • 次エラーメッセージを確認:@order.erros.full_messages を叩く
[3] pry(#<OrdersController>)> @order.errors.full_messages
=> ["Price can't be blank"]
[4] pry(#<OrdersController>)> 

「"Price can't be blank"」のエラーメッセージが生成されることがわかりました

未経験でのWebエンジニア転職を目指して、プログラミングを学習しています。日々の学習した内容をアウトプット用にはてなを書き始めました。TECH CAMP 102 期学生

マークダウンの書き方の練習

クインペッパー
  • 箇条書き
  • ダイナソん
  • 目が飛び出す
  • 美しい白鳥
  • クインのドレス
  • クインのキラキラ王冠
マークダウン記法の練習
  • ハイフン、プラス、アスタリスクのいずれかで箇条書きリストを記述可能。 ※ハイフン、プラス、アスタリスクと箇条書きの項目の間には半角スペースを1つ入れること
  • ハイフン、プラス、アスタリスクのいずれかで箇条書きリストを記述可能。 ※ハイフン、プラス、アスタリスクと箇条書きの項目の間には半角スペースを1つ入れること
  • ハイフン、プラス、アスタリスクのいずれかで箇条書きリストを記述可能。 ※ハイフン、プラス、アスタリスクと箇条書きの項目の間には半角スペースを1つ入れること

  • リスト

    • ネストリスト1
      • ネストリスト1_1
        • ネストリスト1_1_1
        • ネストリスト1_1_2
    • ネストリスト2
  • 数値ドット+半角スペース+内容で   数値付き箇条書きできる 1

    1. . 数値ドット+半角スペース+内容で   数値付き箇条書きできる 3
    2. . 数値ドット+半角スペース+内容で   数値付き箇条書きできる 2
  • 数値ドット+半角スペース+内容で   数値付き箇条書きできる 3
    1. . 数値ドット+半角スペース+内容で   数値付き箇条書きできる 3
  • 数値ドット+半角スペース+内容で   数値付き箇条書きできる 3

  • 番号付きリスト1

    1. 番号付きリスト1_1
      1. 番号付きリスト1_2
    2. 番号付きリスト1_3
  • 番号付きリスト2
  • 番号付きリスト3

お世話になります。xxxです。

ご連絡いただいた、バグの件ですが、仕様です。

お世話になります。

ご連絡ありがとう

強調強調強調アスタリスク1つ

水平線


強調強調強調アスタリスク2つ


強調強調強調アスタリスク3つ

水平線





https://www.google.co.jp/

取り消し線チルダ2個

header1 header2 header3
align left align right align center
a b c
header1 header2 header3
align left align right align center
a b c
コード 名前 誕生日
1 山田太郎 1/1
2 山田花子 2/29
99 その他 12/31
中央寄せ 左寄せ 右寄せ

ユーザー管理機能の実装について

ユーザー管理機能の実装について

  • ユーザーログイン機能

サインアウト、ログインしていない場合(条件分岐)からログイン画面への遷移時

使用するメソッドは図のように

https://i.gyazo.com/e832d0af5b40b8828db8eb74403cdf58.png

  • ユーザーの名前をDBへ保存できるように設定

deviseコントローラーに適応できるようにするために、全コントローラーの共通処理を記述し行えるapplication_controllerを編集する 下の各図

deviseコントロラーから呼び出された時使用するヘルパーメソッド

https://i.gyazo.com/530c603721cdeb0072bd6820e4a65313.png

 

before_action処理を行う

https://i.gyazo.com/afc419240b5255928c4ff8b28067cf2e.png

devise_parameter_sanitizerメソッドとpermit(特定のカラムを許容するメソッド)組み合わせることで新規登録(:sign_up)ユーザー情報(:name)を取得する

https://i.gyazo.com/e3b0acecf0ea1392016137a599ff9a0b.png

https://i.gyazo.com/2b54a70e5e4621df18d38db1ed9248a4.png

自己研鑚のため発信しておりますので、不足の部分があるので、ご了承ください〜

じゃあまた明日〜

Gemのdeviseを用いてユーザー管理機能の実装 

  Gemのdeviseを用いてユーザー管理機能の実装                                    

  deviseのインストール
  • Gemfileにdeviseを記述する

    https://i.gyazo.com/9aa26d763da6ee008add6f249a6b7d7b.png

  •  deviseをgemにinstall

     ターミナル上:

        bundle install

  • railsアプリケーションにdeviseを導入する

  ターミナル上: 

        rails s

  (deviseを導入する前、サーバーを再起動するため)

                           rails g devise:install 

 

  ユーザーテーブルの作成 

  • ターミナル上: rails g devise user

このコマンドでuserモデル(テーブル)と共にmigrateファイルやroutesもが生成する

https://i.gyazo.com/86292443849e8797b95bf07a8a4a7992.png

  • 必要のnameカラムをuserテーブルに追加する

下図の通りmigrateファイル中のemail,passwordカラムがもう生成されていた

https://i.gyazo.com/48222171d80692e0a5b4dc991e13d458.png

ターミナル上:rails db:migrate 実行

 

今日はここまでにしよう。。。