学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

Rails導入編 | カート機能の実装 | 17 | カートの仕上げ

↓↓クリックして頂けると励みになります。




16 | エラー処理】 << 【ホーム】 >> 【18 | Ajax





カートを空にする機能を実装します。
まずは、カートに入れられた複数の商品を1つにまとめるという処理を行います。
マイグレーションファイルを作成します。
ターミナルで以下のコマンドを入力して下さい。
コマンド
rails generate migration combine_items_in_cart

~/Desktop/Rails7_1/SampleCart $ rails generate migration combine_items_in_cart

      invoke  active_record
      create    db/migrate/20240119052941_combine_items_in_cart.rb



生成されたファイルの「change()」メソッドを「up()」「down()」という別のメソッドに書き換えなます。
「SampleCart/db/migrate」フォルダに作成された「20240119052941_combine_items_in_cart.rb」ファイルを以下のように編集します。


記述編集 【SampleCart/db/migrate/20240119052941_combine_items_in_cart.rb】

class CombineItemsInCart < ActiveRecord::Migration[7.1]

  def up
    # カート内に1つの商品に対して複数のLineItemがあった場合は1つのLineItemに置き換える
    Cart.all.each do |cart|
      # カート内の各商品の数をカウントする
      sums = cart.line_items.group(:good_id).sum(:quantity)
  
      sums.each do |good_id, quantity|
        if quantity > 1
          # 個別のLineItemを削除する
          cart.line_items.where(good_id: good_id).delete_all
  
          # 1つのLineItemに置き換える
          item = cart.line_items.build(good_id: good_id)
          item.quantity = quantity
          item.save!
        end
      end
    end
  end

  def down
    # 数量>1 のLineItemを複数のLineItemに分割する
    LineItem.where("quantity >1").each do |line_item|
  
      # 個別のLineItemを追加する
      line_item.quantity.times do
        LineItem.create(
          cart_id: line_item.cart_id,
          good_id: line_item.good_id,
          quantity: 1
        )
      end
  
      # 元のLineItemを削除する
      line_item.destroy
    end
  end

end



マイグレーションを実行します。
ターミナルで以下のコマンドを入力します。
コマンド
rails db:migrate

~/Desktop/Rails7_1/SampleCart $ rails db:migrate

== 20240119052941 CombineItemsInCart: migrating ===============================
== 20240119052941 CombineItemsInCart: migrated (0.1274s) ======================



upメソッドでは数量が2以上のLineItemを見つけ、このカートと商品にそれぞれ数量1で新しいLineItemを追加し最後に元のLineItemを削除します。
このように、通常のマイグレーションを行うと、upメソッドが実行され、「rails db:rollback」としてロールバックを行うと、downメソッドが実行されます。

モデル

「total_price()」メソッドを「line_itemモデル」に記述します。
「SampleCart/app/models」フォルダにある「line_item.rb」ファイルを以下のように編集します。


記述追加 【SampleCart/app/models/line_item.rb】

class LineItem < ApplicationRecord
  belongs_to :good
  belongs_to :cart

  def total_price
    good.price * quantity
  end

end



同じく「total_price()」メソッドを「cartモデル」にも記述します。
「SampleCart/app/models」フォルダにある「cart.rb」ファイルを以下のように編集します。


記述追加 【SampleCart/app/models/cart.rb】

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

	def add_good(good)
		current_item = line_items.find_by(good_id: good.id)
		if current_item
			current_item.quantity += 1
		else
			current_item = line_items.build(good_id: good.id)
		end
		current_item

	end

	def total_price
		line_items.to_a.sum {|item| item.total_price }
	end
end



カートコントローラの「destroy()」メソッドを修正してセッションをクリーンにする記述を追加します。
ユーザの同意を得てカートを削除し、セッションからカートを削除したうえでフラッシュメッセージを出し、トップページにリダイレクトするようにします。
「SampleCart/app/controllers」フォルダにある「carts_controller.rb」ファイルを以下のように編集します。
変更した部分は52行目の「destroy()」メソッドのみです。


記述編集 【SampleCart/app/controllers/carts_controller.rb】52行目

class CartsController < ApplicationController
  before_action :set_cart, only: %i[ show edit update destroy ]
  rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart

  # GET /carts or /carts.json
  def index
    @carts = Cart.all
  end

  # GET /carts/1 or /carts/1.json
  def show
  end

  # GET /carts/new
  def new
    @cart = Cart.new
  end

  # GET /carts/1/edit
  def edit
  end

  # POST /carts or /carts.json
  def create
    @cart = Cart.new(cart_params)

    respond_to do |format|
      if @cart.save
        format.html { redirect_to cart_url(@cart), notice: "Cart was successfully created." }
        format.json { render :show, status: :created, location: @cart }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @cart.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /carts/1 or /carts/1.json
  def update
    respond_to do |format|
      if @cart.update(cart_params)
        format.html { redirect_to cart_url(@cart), notice: "Cart was successfully updated." }
        format.json { render :show, status: :ok, location: @cart }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @cart.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /carts/1 or /carts/1.json
  def destroy
    @cart.destroy if @cart.id == session[:cart_id]
    session[:cart_id] = nil

    respond_to do |format|
      format.html { redirect_to markets_index_url, notice: 'カートが空になりました。' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_cart
      @cart = Cart.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def cart_params
      params.fetch(:cart, {})
    end

    def invalid_cart
      logger.error "無効なカート(#{params[:id]})にアクセスしようとしました。"
      redirect_to markets_index_url, notice: '無効なカートです。'
    end
  
end



次にカートビューを編集します。
「SampleCart/app/views/carts/show.html.erb」ファイルを以下のように編集します。


記述編集 【SampleCart/app/views/carts/show.html.erb】

<div class="container mt-4">
  <div class="card">
    <div class="card-body">
      <div class="card-title h5 mb-4"><strong>現在のカート</strong></div>
      <% @cart.line_items.each do |item| %>
        <div class="card mb-2">
          <div class="card-body">
            <%= item.good.title %>
            <span class="badge bg-warning"><%= number_to_currency(item.good.price) %></span>
            × 
            <span class="badge bg-primary"><%= item.quantity %></span><span class="badge bg-danger"><%= number_to_currency(item.total_price) %></span>
          </div>
        </div>
      <% end %>
      <div class="container mt-4">
        <span class="badge bg-secondary">合計金額</span> <span class="badge bg-success fs-5"><%= number_to_currency(@cart.total_price) %></span>
      </div>
      <div class="mt-4">
        <%= button_to 'カートを空にする', @cart, method: :delete, class: 'btn btn-outline-danger', data: { turbo: false }, form: { onSubmit: "return check()" } %>
      </div>

    </div>
  </div>
</div>

<script>
  function check(){
    if(window.confirm('カートを空にしますか?')){ 
      return true;
    }
    else{
      window.alert('キャンセルされました'); 
      return false; 
    }
  }
</script>



ブラウザの表示を確認します。
http://localhost:3000/carts/8

カート表示確認
カート表示確認



これで「カートを空にする」ボタンを押すとカートの内容が破棄されてトップページに戻り、「カートが空になりました。」というフラッシュメッセージが表示されます。

カートを空にする
カートを空にする




16 | エラー処理】 << 【ホーム】 >> 【18 | Ajax





↓↓クリックして頂けると励みになります。