2
1

More than 1 year has passed since last update.

4 Railsを用いたカート機能の作成方法

Last updated at Posted at 2022-11-09

事前準備

カートに関するモデル , テーブルをこのように定義する。

schema.rb

create_table "cart_items", force: :cascade do |t|
    t.integer "item_id"
    t.integer "end_user_id"
    t.integer "amount"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

cart_item.rb

class CartItem < ApplicationRecord
  belongs_to :end_user
  belongs_to :item
end

アソシエーションを結ぶ

cart_item.rb

 belongs_to :end_user
 belongs_to :item

item.rb

 has_many :cart_items

end_user.rb

 has_many :cart_items

1 カートに商品を追加する

(1)フォームを作成する

hiddn_fieldを用いてどの商品をカートへ追加するかという情報を顧客が入力することなく
送ることができる。

items/show.html.erb
items_controller.rb

  def show
    @item = Item.find(params[:id])
    ↓の部分を追記する
    @cart_item_new = CartItem.new
  end

items/show.html.erb

<%= render "public/shared/header" %>
<%= attachment_image_tag @item, :image, :fill, 300, 300, format: 'jpeg' %>
<%= @item.name %>
<%= @item.explanation %>
<%= @item.with_tax_price %>
<%= form_with model:@cart_item_new, url: "/cart_items", local:true do |f| %>

↓の部分を追記する

<%= form_with model:@item_new, local:true do |f| %>
 <%= f.hidden_field :item_id, value: @item.id %>
  <%= f.select :amount, *[1..10] %>
<% end %>
ポイント

1 form_withに使われるプロパティ、キーはカラム名を指す
2 hidden_fieldに入れるのは商品の外部キー => 商品の名前,値段をまとめて入れることができる

3

f.hidden_field :保存したいカラム先, value: 保存したい値 

4 作ろうとしている投稿フォームは、カートに関する投稿フォーム

@item_new = Item.newだと商品に関する投稿フォームを作ろうとしているので×

(2)カートの保存機能作成

def create
    cart_item = CartItem.new(cart_item_params)
    cart_item.end_user_id = current_end_user.id
    cart_item.item_id = cart_item_params[:item_id]
    if CartItem.find_by(item_id: params[:cart_item][:item_id]).present?
      cart_item = CartItem.find_by(item_id: params[:cart_item][:item_id])
      cart_item.amount += params[:cart_item][:amount].to_i
      cart_item.update(amount: cart_item.amount)
      redirect_to cart_items_path
    else
      cart_item.save
      redirect_to cart_items_path
    end
  end

  private
  def cart_item_params
    params.require(:cart_item).permit(:item_id, :amount)
  end

この時のパラメータ
<ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"Sn+tcxnjesxqLc5hTpKKiOdWCL8d0kBj1aJ06LLzwk1Wo4KuP6rHvH+1cXXNkXXC9a/wv8sjOGX5H3qZ5PtG4A==", "cart_item"=><ActionController::Parameters {"item_id"=>"1", "amount"=>"1"} permitted: false>, "commit"=>"カートに入れる", "controller"=>"public/cart_items", "action"=>"create"} permitted: false
cart_item_params
<ActionController::Parameters {"item_id"=>"1", "amount"=>"1"} permitted: true>
CartItem.new(cart_item_params)
#<CartItem:0x00007f45dd908218 id: nil, item_id: 1, end_user_id: nil, amount: 1, created_at: nil, updated_at: nil>
ポイント

1 end_user_idが空より、入れてあげる必要がある

cart_item.end_user_id = current_end_user.id

2

cart_item_params[:item_id]

 
 
 [:item_id]と付けることで、cart_item_paramsの中にあるitem_idのみを取得する。

同一商品がある場合の処理を実装

 if CartItem.find_by(item_id: params[:cart_item][:item_id]).present?
      cart_item = CartItem.find_by(item_id: params[:cart_item][:item_id])
      cart_item.amount += params[:cart_item][:amount].to_i
      cart_item.update(amount: cart_item.amount)
      redirect_to cart_items_path

ポイント
1

if CartItem.find_by(item_id: params[:cart_item][:item_id]).present?

item_id: params[:cart_item][:item_id]より

prameters cart_item"=>{"item_id"=>"~"

↑の~ていう数字がCartItemのitem_idというカラムに含まれているのかif文で判断

pry(#<Public::CartItemsController>)> params[:cart_item][:item_id]
=> "1"

この場合、item_idに1があるかで条件分岐させる

2

cart_item = CartItem.find_by(item_id: params[:cart_item][:item_id])

if文の条件を満たす物をcart_itemという変数に入れている。

3

cart_item.amount += params[:cart_item][:amount].to_i

元々入っていた商品の個数(amount)に
params[:cart_item][:amount]の値を数字型にして加える

4

 cart_item.update(amount: cart_item.amount)

saveは、新しくレコードを作る=>今回は、saveではなく、update
今回は、amountだけをupdateしたい。update(更新したいカラム名:更新したい値が入った変数)
と記述することで特定のものをupdateできる。

2 カート内の商品を表示する

cart_items_controller.rb

 def index
    @cart_item = CartItem.all
  end
   <table class="table table-bordered">
      <tr class="table-active">
         <th>商品名</th>
         <th>単価(税込み)</th>
         <th>数量</th>
         <th>小計</th>
         <!--タグの中にbrタグを入れることで空白のセルを作れる-->
         <th><br></th>
      </tr>
       <% @sum = 0 %>
       <% @cart_item.each do |cart_item| %>
         <tr>
            <td>
               <%= attachment_image_tag cart_item.item, :image, :fill, 300, 300, format: 'jpeg' %>
               <%= cart_item.item.name %>
            </td>
            <td><%= cart_item.item.with_tax_price %></td>
            <td>
               <%= form_with model:cart_item, url:cart_item_path(cart_item.id) , local:true do |f| %>
                  <%= f.select :amount, *[1..10] %>
                  <%= f.submit "変更" , class: "btn btn-success" %>
               <% end %>
            </td>
            <td><%= cart_item.subtotal %></td>
            <td><%= link_to "削除", destroy_cart_item_path(cart_item.id) ,  method: :delete , "data-confirm" => "商品を削除しますか?" , class: "btn btn-danger"%></td>
         </tr>
         <% @sum += cart_item.subtotal %>
   </table>
<% end %>
<%= link_to "買い物を続ける" , root_path , class: "btn btn-primary" %>
合計金額 <%= @sum %>

<%= link_to "情報入力に進む" , orders_new_path , class: "btn btn-success" %>
ポイント
1 数量変更フォームの作り方
 <%= form_with model:cart_item, url:cart_item_path(cart_item.id) , local:true do |f| %>
   <%= f.select :amount, *[1..10] %>
   <%= f.submit "変更" , class: "btn btn-success" %>
 <% end %>

form_withを作るための変数は、each文で作れたcart_itemというブロック変数を用いる
cart_items_controller.rbでインスタンス変数を定義する必要なし!!

数量を変更するアクション

def update
    cart_item = CartItem.find(params[:id])
    cart_item.update(cart_item_params)
    redirect_back(fallback_location: root_path)
end
2 合計金額を表示させる方法
1 <% @sum = 0 %>と定義する
2 <% @sum += cart_item.subtotal %>で商品の金額を足す(each文の中に入れることでカートの種類分繰り返す)
3 <%= @sum %>で表示する

cart_item.rb
小計を求めるメソッド

  def subtotal
    item.with_tax_price * amount
  end

item.rb
消費税を求めるメソッド

def with_tax_price
   (price_excluding_tax * 1.1).floor
end
2 カートに入ってる商品を表示する方法
<td>
   <%= attachment_image_tag cart_item.item, :image, :fill, 300, 300, format: 'jpeg' %>
   <%= cart_item.item.name %>
</td>
<td><%= cart_item.item.with_tax_price %></td>

schema.rbより、

create_table "cart_items", force: :cascade do |t|
    t.integer "item_id"
    t.integer "end_user_id"
    t.integer "amount"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

create_table "items", force: :cascade do |t|
    t.integer "genre_id"
    t.string "name"
    t.integer "price_excluding_tax"
    t.text "explanation"
    t.string "image_id"
    t.boolean "is_active", default: true
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

cart_itemには、amount , item_id , end_user_idしかない
=> cart_itemでは、商品の画像 , 名前 を表示することができない
よって、アソシエーションを用いて、cart_itemに関連するitemから画像と商品の名前を持ってくる

3 カート内の商品を全て削除する

 def destroy_all
    CartItem.destroy_all
    redirect_back(fallback_location: root_path)
 end
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1