<<前 [TOP] 次>>
商品をセッションカートという一時的な保存場所に格納出来るようなシステムを作っていきます。
Railsではアクセスしている人が実行するすべての操作を追跡できるようにするためにsessionと呼ばれるものを使用しています。
sessionはコントローラ内にハッシュに似たような形で保持しています。
この機能を使って商品を格納するためのカートを作成してみます。
コマンドプロンプトで「bin」フォルダに移動して「rails generate scaffold cart
」と入力します。
途中「scaffolds.scss」を上書きするか聞いてくるので、「n」と入力して上書きしないようにします。
次に「rails db:migrate
」コマンドでcartsテーブルをデータベースに作成します。
MySQLに接続してcartテーブルの内容を見てみます。
「id」「created_at」「updated_at」の3つのフィールドがあるのが分かります。
「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」と入力して上書きしないようにします。
データベースに「LineItem」テーブルを作成します。
コマンドプロンプトで「bin」フォルダに移動して「rails db:migrate
」と入力します。
「line_items」テーブルを確認してみます。
line_itemsテーブルとgoodsテーブルを外部キーの参照によってリンクさせています。
line_itemsテーブルの「good_id」フィールドとgoodsテーブルの「id」フィールドがリンクしています。
続いて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; }
再び商品をカートに追加して確認します。
↓↓クリックして頂けると励みになります。