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

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

Ruby on Rails 6.0によるWebアプリケーション開発20 カートの作成

<<前  [TOP]  次>>


商品をセッションカートという一時的な保存場所に格納出来るようなシステムを作っていきます。
Railsではアクセスしている人が実行するすべての操作を追跡できるようにするためにsessionと呼ばれるものを使用しています。
sessionはコントローラ内にハッシュに似たような形で保持しています。
この機能を使って商品を格納するためのカートを作成してみます。


コマンドプロンプトで「bin」フォルダに移動して「rails generate scaffold cart」と入力します。

カートの作成
カートの作成


途中「scaffolds.scss」を上書きするか聞いてくるので、「n」と入力して上書きしないようにします。
「scaffolds.scss」を上書き保存しない
「scaffolds.scss」を上書き保存しない


次に「rails db:migrate」コマンドでcartsテーブルをデータベースに作成します。
cartsテーブルをデータベースに作成
cartsテーブルをデータベースに作成


MySQLに接続してcartテーブルの内容を見てみます。
「id」「created_at」「updated_at」の3つのフィールドがあるのが分かります。
Cartテーブルの内容
Cartテーブルの内容


「C:\Rails6\work\shop\app\controllers」フォルダにある「concerns」フォルダに「current_cart.rb」ファイルを新規作成して保存します。
「current_cart.rb」ファイルの内容は以下の通りです。


【C:\Rails6\work\shop\app\controllers\current_cart.rb】

module CurrentCart

	private

	def set_cart
		@cart = Cart.find(session[:cart_id])
	rescue ActiveRecord::RecordNotFound
		@cart = Cart.create
		session[:cart_id] = @cart.id
	end

end



「set_cart()」メソッドの最初の部分では「sessionオブジェクト」から「:cart_id」を取得し、そのidに対応するカートを探しています。
カートのレコードが見つからない場合このメソッドは新しいCartを作成してidをセッションに格納し、そのカートを返します。
「set_cart()」メソッドは「module CurrentCart」としてモジュールに配置して「private」にしています。
この処理によりコントローラー間で共通のコードを共有できます。


商品とカートの関連付けを行います。
コマンドプロンプトで「bin」フォルダに移動して「rails generate scaffold LineItem good:references cart:belongs_to」と入力します。

商品とカートの関連付け
商品とカートの関連付け


途中で「scaffolds.scss」を上書きするか聞いてくるので、今回も「n」と入力して上書きしないようにします。
「scaffolds.scss」を上書き「n」
「scaffolds.scss」を上書き「n」


データベースに「LineItem」テーブルを作成します。
コマンドプロンプトで「bin」フォルダに移動して「rails db:migrate」と入力します。
LineItemテーブルの作成
LineItemテーブルの作成


「line_items」テーブルを確認してみます。
line_itemsテーブルとgoodsテーブルを外部キーの参照によってリンクさせています。
line_itemsテーブルの「good_id」フィールドとgoodsテーブルの「id」フィールドがリンクしています。
「line_items」テーブルの確認
「line_items」テーブルの確認


続いてLineItemからcartsおよびgoodsテーブルへの逆方向リンクを指定します。
この指定には「C:\Rails6\work\shop\app\models」フォルダにある「line_item.rb」ファイル内で「belong_to()」宣言を2回使います。
「belong_to」はRailsに「line_itemテーブル」の行はcartsテーブルとgoodsテーブル内にある行の子であることを通知します。
この記述は自動で生成されているので編集する必要はありません。


【C:\Rails6\work\shop\app\models\line_item.rb】

class LineItem < ApplicationRecord
  belongs_to :good
  belongs_to :cart
end



「C:\Rails6\work\shop\app\models」フォルダ内に新規作成された「cart.rb」ファイルを開いて以下のように「has_many()」の呼び出しを追加します。
「has_many :line_items」という部分は1つのカートに多数の品目が関連付けられることを意味しています。
「dependent: :destroy」はカートを破棄してデータベースから削除したときにはそのカートに関連付けられている品目も全て削除されるようにしたいということです。


【C:\Rails6\work\shop\app\models\cart.rb】

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



Goodモデルに「has_many」を追加します。
いずれカートが増えた時、各商品がその商品を参照する多数のLineItemを持つ可能性があるからです。
検証コードを使ってlineItemから参照されている商品が削除されないようにします。
「good.rb」ファイルを以下のように編集してください。


【C:\Rails6\work\shop\app\models\good.rb】

class Good < ApplicationRecord

	has_many :line_items

	before_destroy :ensure_not_referenced_by_any_lin_item

	validates :title, :description, :image_url, presence: true
	validates :price, numericality: {greater_than_or_equal_to: 1}
	validates :title, uniqueness: true

	validates :image_url, allow_blank: true, format: {
		with: %r{\.(gif|jpg|png)\z}i,
		message: 'はGIF、JPG、PNG画像のURLでなければなりません。'
	}

	private
	# この商品を参照しているLineItemがないことを確認する
	def ensure_not_referenced_by_any_line_items

		unless line_items.empty?
			errors.add(:base, '品目が存在します。')
			throw :abort
		end
	end
end



ここでは商品が多数の品目を持つことを「has_many」で宣言してから「ensure_not_referenced_by_any_lin_item」というフックメソッドを定義しています。
フックメソッドとはRailsから自動的に呼び出されるメソッドです。
この場合はRailsがデータベースの行を削除しようとしたときに呼び出されます。
このフックメソッドでfalseが返されると行は削除されません。
「errors」オブジェクトはvalidates()でエラーメッセージが格納されるのと同じ場所です。
エラーは個別の属性と関連付けることもできますが、この場合はベースオブジェクト自体と関連付けています。


次は各商品に「カートに入れるボタン」を追加します。
これには「button()」メソッドを使います。
Railsならコントローラの名前に「_path」と付け加えるだけであとは任せてしまうことができます。
ここでは「line_items_path」を使用します。
「C:\Rails6\work\shop\app\views\market」フォルダにある「index.html.erb」ファイルを以下のように編集します。
「<%= button_to 'カートに入れる', line_items_path(good_id: good ) %>」の1行を追加しただけです。


【C:\Rails6\work\shop\app\views\market\index.html.erb】

<% if notice %>
	<aside id="notice"><%= notice %></aside>
<% end %>

<h1>Railsはじめてマート 商品カタログ</h1>
<br>
<ul class="catalog">

	<% @goods.each do |good| %>

		<li>

			<%= image_tag(good.image_url, width: 140) %>
			<h2><%= good.title %></h2>
			<p><%= sanitize(good.description) %></p>
			<div class="price">
				<%= (good.price).to_i %>円
				<%= button_to 'カートに入れる', line_items_path(good_id: good ) %>
			</div>

		</li>

	<% end %>

</ul>



ブラウザで「http://localhost:3000/」にアクセスして表示を確認してみます。

ブラウザの表示
ブラウザの表示


CSSに記述を追加します。
「C:\Rails6\work\shop\app\assets\stylesheets」フォルダにある「market.scss」を以下のように変更します。


【C:\Rails6\work\shop\app\assets\stylesheets\market.scss】

// Place all the styles related to the Market controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/

.market {
	max-width: 80em;
	ul.catalog {
		border-top: solid 0.250em;
		list-style : none;
		padding: 0;
		margin: 0;
		li {
			padding: 0;
			margin: 0;
			border-bottom: solid thin #ddd;

			&::after {
				clear: both;
				content: " ";
				display: block;
			}

			img {
				float: left;
				padding: 1em;
				margin-right: 1em;
				margin-bottom: 1em;
				box-shadow: 0.176em 0.176em 0.354em 0px rgba(0,0,0,0.75);
			
			}
			.price {
				font-size: 1.414em;
			}
		}
	}
}

form, div {
	display: inline;
}

input[type="submit"] {
	background-color: #db7093;
	border-radius: 0.554em;
	border: solid thin #c71585;
	color: white;
	font-size: 0.8em;
	padding: 0.354em 1em;
	
}

input[type="submit"]:hover {
	background-color: #ffb6c1;
}



ブラウザで「http://localhost:3000/」にアクセスして表示を確認してみます。

ブラウザの表示
ブラウザの表示


「LineItemコントローラ」を修正します。
現在のセッションのショッピングカートを見つけて(ショッピングカートがまだ存在しない場合は新たに作成)選択された商品をカートに追加したうえでカートの内容を表示するようにします。
「C:\Rails6\work\shop\app\controllers」にある「line_items_controller.rb」ファイルを以下のように修正します。
最初の「include CurrentCart」「before_action :set_cart, only: [:create]」の2行を追加したのと「create()」メソッドの部分を修正しています。



【C:\Rails6\work\shop\app\controllers\line_items_controller.rb】

class LineItemsController < ApplicationController

	include CurrentCart
	before_action :set_cart, only: [:create]
	before_action :set_line_item, only: [:show, :edit, :update, :destroy]

  # GET /line_items
  # GET /line_items.json
  def index
    @line_items = LineItem.all
  end

  # GET /line_items/1
  # GET /line_items/1.json
  def show
  end

  # GET /line_items/new
  def new
    @line_item = LineItem.new
  end

  # GET /line_items/1/edit
  def edit
  end

  # POST /line_items
  # POST /line_items.json
  def create

	good = Good.find(params[:good_id])
	@line_item = @cart.line_items.build(good: good)

    respond_to do |format|
      if @line_item.save
        format.html { redirect_to @line_item.cart, notice: '品目が作成されました。' }
        format.json { render :show, status: :created, location: @line_item }
      else
        format.html { render :new }
        format.json { render json: @line_item.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /line_items/1
  # PATCH/PUT /line_items/1.json
  def update
    respond_to do |format|
      if @line_item.update(line_item_params)
        format.html { redirect_to @line_item, notice: '品目が更新されました。' }
        format.json { render :show, status: :ok, location: @line_item }
      else
        format.html { render :edit }
        format.json { render json: @line_item.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /line_items/1
  # DELETE /line_items/1.json
  def destroy
    @line_item.destroy
    respond_to do |format|
      format.html { redirect_to line_items_url, notice: '品目を破棄しました。' }
      format.json { head :no_content }
    end
  end

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

    # Only allow a list of trusted parameters through.
    def line_item_params
      params.require(:line_item).permit(:good_id, :cart_id)
    end
end



実際に「カートに入れる」ボタンを押してみます。

カートに入れるボタンを押す
カートに入れるボタンを押す


カートの表示を変更していきます。
「C:\Rails6\work\shop\app\views\carts」フォルダにある「show.html.erb」ファイルを以下のように変更します。


【C:\Rails6\work\shop\app\views\carts\show.html.erb】

<% if notice %>
	<aside id="notice"><%= notice %></aside>
<% end %>

<h2>カートの内容</h2>

<ul>
	<% @cart.line_items.each do |item| %>
		<li><%= item.good.title %></li>
	<% end %>
</ul>



CSSの変更を行います。
「C:\Rails6\work\shop\app\assets\stylesheets」フォルダにある「application.css」ファイルに以下の項目を追加します。


【C:\Rails6\work\shop\app\assets\stylesheets\application.css】

.notice, #notice {
	background: #ffb;
	border-radius: 0.5em;
	border: solid 0.177em #882;
	color: #882;
	font-weight: bold;
	margin-bottom: 1em 1.414em;
	text-align: center;

}



再び商品をカートに追加して確認します。

カートの表示
カートの表示


<<前  [TOP]  次>>