事前準備
カートに関するモデル , テーブルをこのように定義する。
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