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


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

schema.rb
```Ruby
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
```Ruby
class CartItem < ApplicationRecord
  belongs_to :end_user
  belongs_to :item
end
```

####   アソシエーションを結ぶ
cart_item.rb
```Ruby
 belongs_to :end_user
 belongs_to :item
```
item.rb
```Ruby
 has_many :cart_items
```

end_user.rb
```Ruby
 has_many :cart_items
```
## 1 カートに商品を追加する
#### (1)フォームを作成する
hiddn_fieldを用いてどの商品をカートへ追加するかという情報を顧客が入力することなく
送ることができる。

items/show.html.erb
items_controller.rb
```Ruby
  def show
    @item = Item.find(params[:id])
    ↓の部分を追記する
    @cart_item_new = CartItem.new
  end
```

items/show.html.erb
```Ruby
<%= 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 %>
```


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

3 
```
f.hidden_field :保存したいカラム先, value: 保存したい値 
```
4 作ろうとしている投稿フォームは、カートに関する投稿フォーム
  ```
  @item_new = Item.newだと商品に関する投稿フォームを作ろうとしているので×
  ```

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

```Ruby
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>
 ```
##### <font color="Red">ポイント</font>
1 end_user_idが空より、入れてあげる必要がある
 ```Ruby
 cart_item.end_user_id = current_end_user.id
 ```
2
```Ruby
cart_item_params[:item_id]
```
　
　
　[:item_id]と付けることで、cart_item_paramsの中にあるitem_idのみを取得する。

### 同一商品がある場合の処理を実装
```Ruby
 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
 ```
<font color="Red">ポイント</font>
1
```Ruby
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
```Ruby
cart_item = CartItem.find_by(item_id: params[:cart_item][:item_id])
```
if文の条件を満たす物をcart_itemという変数に入れている。


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

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

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

cart_items_controller.rb
```Ruby
 def index
    @cart_item = CartItem.all
  end
```

```Ruby
   <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" %>
```

##### <font color="Red">ポイント</font>

###### 1 数量変更フォームの作り方
```Ruby
 <%= 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でインスタンス変数を定義する必要なし!!

数量を変更するアクション
```Ruby
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
小計を求めるメソッド
```Ruby
  def subtotal
    item.with_tax_price * amount
  end
```

item.rb
消費税を求めるメソッド
```Ruby
def with_tax_price
   (price_excluding_tax * 1.1).floor
end
```


###### 2 カートに入ってる商品を表示する方法
```Ruby
<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より、
```Ruby
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 カート内の商品を全て削除する
```Ruby
 def destroy_all
    CartItem.destroy_all
    redirect_back(fallback_location: root_path)
 end
```