はじめに
- URI設計
- コントローラーに寄りすぎたロジック
の二点を改善しました。
ER図
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
ルーティング
route.rb
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
のパスと現在のカートを紐付けても良いかもしれません。
モデル
models/cart.rb
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
models/item.rb
class Item < ApplicationRecord
belongs_to :cart
belongs_to :product
end
バリデーション等の処理を適宜追加します。
コントローラー
モデルの時点でほぼ完成なのですが、一応カート内商品の操作周りのコントローラーは以下となります。
app/controllers/cart_items_controller.rb
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で表示して完成です。