🦧

RailsでECサイトのカート機能を実装する(改訂版)

2021/08/18に公開

はじめに

RailsでECサイトのカート機能を実装する
での

  • URI設計
  • コントローラーに寄りすぎたロジック
    の二点を改善しました。

ER図

Userモデルは除いています。
ec.png

Cart、Item、Productテーブルのmigrationファイルの作成

$ rails g model Cart
$ rails g model Product name:string price:integer
$ rails g model Item cart:references product:references quantity:integer
$ rails db:migarate

ルーティング

  resources :carts, only: %i[create destroy] do
    resources :items, only: %i[create destroy update index], param: :item_id, controller: :cart_items
  end
  • id:cart_idを持つCartを示すリソースのパスはcarts/:cart_id
  • id:cart_idを持つCartに関連するid:item_idを持つItemを示すリソースのパスはcarts/:cart_id/items/:item_id
    といった感じでRESTfulな設計にします。
    ただ、cart/:idといった形で商品カートにアクセスできるインターフェースを用意するべきなのかどうかは疑問です。セッションで紐付けたcartしかアクセスしないのであれば/cartのパスと現在のカートを紐付けても良いかもしれません。

モデル

class Cart < ApplicationRecord
  has_many :items, dependent: :destroy

  #    カートアイテムの追加
  def add_item(product_id:, quantity:)
    # カートに入っていない商品を追加する場合はItemレコードを新規作成
    item = items.find_by(product_id: product_id) || items.build(product_id: product_id)
    item.quantity += quantity.to_i
    item.save
  end

  #    カートアイテムの更新
  def update_item(product_id:, quantity:)
    items.find_by(product_id: product_id).update(quantity: quantity.to_i)
  end

  #    カートアイテムの削除
  def delete_item(product_id:)
    items.find_by(product_id: product_id).destroy
  end

  #    カートの合計の算出
  def total_price
    items.sum("quantity*price")
  end
end
class Item < ApplicationRecord
  belongs_to :cart
  belongs_to :product
end

バリデーション等の処理を適宜追加します。

コントローラー

モデルの時点でほぼ完成なのですが、一応カート内商品の操作周りのコントローラーは以下となります。

class CartItemsController < ApplicationController
  def index
    @total_price = my_cart.total_price
    @items = my_cart.items
  end

  def create
    if my_cart.add_item(product_id: params[:product_id], quantity: params[:quantity])
      # カートアイテムの追加に成功した時の処理
    else
      # 失敗した時の処理
    end
  end

  def update
    if my_cart.update_item(product_id: params[:product_id], quantity: params[:quantity])
      # カートアイテムの更新に成功した時の処理
    else
      # 失敗した時の処理
    end
  end

  def destroy
    if my_cart.delete_item(product_id: params[:product_id])
      # カートアイテムの削除に成功した時の処理
    else
      # 失敗した時の処理
    end
  end

  private

  def my_cart
    Cart.find(params[:cart_id])
  end
end

これらをViewで表示して完成です。

Discussion