Rails2.2でサンプルアプリを作ってみる(5)

Rails2.2を利用したサンプルアプリ作成の続きです。

すでにここまで作成が終わっているものとして話を進めていきます。

  1. Rails2.2でサンプルアプリを作ってみる(1) - 積み重ねた日々
  2. Rails2.2でサンプルアプリを作ってみる(2) - 積み重ねた日々
  3. Rails2.2でサンプルアプリを作ってみる(3) - 積み重ねた日々
  4. Rails2.2でサンプルアプリを作ってみる(4) - 積み重ねた日々

今回の作業内容

今回は、以下の変更を行っていきたいと思います。

  • カートを作成する

カートを作成する

商品を追加するためのカートを取得できる必要があるので、StoreController(app/controllers/store_controller.rb)へfind_cartメソッドを追加します。

class StoreController < ApplicationController
  def index
    @products = Product.find_products_for_sale
  end

  private
    def find_cart
      # :cartというsessionハッシュのキーに対応する値を持たない場合、Cartオブジェクトを生成し、sessionにセットする
      session[:cart] ||= Cart.new
    end
end

find_cartはアクションとして公開したくないので、privateにします。(privateの行の下にメソッドを追加すると、privateメソッドになるので注意が必要)

カートがまだ実装されていないので、Cartクラスを実装します。(app/models/cart.rb)

class Cart
  attr_reader :items

  def initialize
    @items = []
  end

  def add_product(product)
    # @items.add(product)と同じ
    @items << product
  end
end

次にカートに商品を追加するアクション(add_to_cart)を追加します。(app/controllers/store_controller.rb)

class StoreController < ApplicationController
  def index
    @products = Product.find_products_for_sale
  end

  def add_to_cart
    @cart = find_cart
    product = Product.find(params[:id])
    @cart.add_product(product)
  end

  private
    def find_cart
      # :cartというsessionハッシュのキーに対応する値を持たない場合、Cartオブジェクトを生成し、sessionにセットする
      session[:cart] ||= Cart.new
    end
end

最後にテンプレートを作成します。(app/views/store/add_to_cart.html.erb)

<h2>Your Pragmatic Cart</h2>
<ul>
  <% for item in @cart.items %>
    <li><%=h item.title %></li>
  <% end %>
</ul>

これでカートができました。

しかし、現状のカートでは、同一の商品をカートへ追加すると、同一商品が複数行にわたって表示されてしまいます。

あらたにカート内の商品と数量を持つCartItemクラスを作成します。また商品名と商品の総額を返すメソッドを用意します。(app/models/cart_item.rb)

class CartItem
  attr_reader :product, :quantity

  def initialize(product)
    @product = product
    @quantity = 1
  end

  def increment_quantity
    @quantity += 1
  end

  def title
    @product.title
  end

  def price
    @product.price * @quantity
  end
end

商品をカートに追加する際に、既にカートに登録された商品であったらカートの商品の数量を増やし、存在しない商品であれば、カートに商品を追加するように変更します。(app/models/cart.rb)

class Cart
  attr_reader :items

  def initialize
    @items = []
  end

  def add_product(product)
    # カート内の商品リストから追加する商品を探す
    # 存在すれば、current_itemに代入し、存在しなければnilが代入される
    current_item = @items.find {|item| item.product == product}
    if current_item
      current_item.increment_quantity
    else
      @items << CartItem.new(product)
    end
  end
end

テンプレートに数量を表示されるように変更します。(app/views/store/add_to_cart.html.erb)

<h2>Your Pragmatic Cart</h2>
<ul>
  <% for item in @cart.items %>
      <li><%= item.quantity %> &times; <%=h item.title %></li>
  <% end %>
</ul>

この状態で、カートに商品を追加すると、以下のエラーが発生します。

これは元のカートの実装時に登録されたセッションデータが古いために、現在のカートの実装では正しく動作しません。

この問題を解消するために、セッションデータを一度クリアします。

$ rake db:sessions:clear
(in /Users/kouichi/Development/Ruby/depot)

クリア後に再度アクセスすると今度はエラーが発生しなくなりました。同じ商品をカートを入れた場合に、複数行になることもなくなりました。

上記で書かれた問題は、開発時だけでなく実稼働時に発生するので、実際にはカートの情報はセッションで管理せず、データベースへ格納し管理するべきです。

次回に続きます。
Rails2.2でサンプルアプリを作ってみる(6) - 積み重ねた日々 へ続きます。