意外に情報がなかったのでメモ。
セレクトボックスのハードコーディングの問題
####Viewファイル
= form_for モデルに基づいたインスタンス変数 do |f|
= f.label :quantity, "数量"
= f.select :quantity, [1,2,3,4,5,6,7,8,9,10]
~~省略~~
AmazonのようなECサイトを構築していて遭遇した問題。
selectの選択肢をハードコーディングすると、
在庫が10個未満の商品でもセレクトボックスでは10個まで選択できるようになってしまう。
collection_selectを使えばいいじゃん←この場合うまくいかない
例えば、在庫数をStocksテーブルのcurrent_stockカラムで管理しているとすると、
def show
@stock = Stock.find(params[:id])
@cart = Cart.new
end
= form_for @cart do |f|
= f.label :quantity, "数量"
= f.collection_select :quantity, @stock.current_stock, :current_stock, :current_stock #エラー
= f.collection_select :quantity, Stock.all, :current_stock, :current_stock #商品の在庫数のリストになってしまう、欲しいデータではない。
collection_selectの第2引数は、モデルを選択する必要があるので、@stock.current_stockではエラーになるし、Stock.allにするとStocksテーブルの全データになってしまう。
配列を生成して、ビューに渡してあげる
選択できる数量の数だけの配列を作っておくことで解決。
今回の場合、在庫数以下かつ10までのセレクトボックスを生成する。
def show
@stock = Stock.find(params[:id])
@cart = Cart.new
@current_stock_array = [] #配列を生成
@stocks.current_stock.times do |quantity| #@stockの現在の在庫数まで、ループを回す。
if quantity < 10 #quantityが10未満かどうか?
@current_stock_array += quantity + 1
#quantityは0からスタートしているので、1足した数を入れる必要がある。
else
break #ループを抜ける
end
end
end
こうすると、在庫数に応じて、セレクトボックスの選択肢が動的に変わる。
####在庫数10以上の場合
@current_stock_array = []
@stocks.current_stock.times do |quantity|
if quantity < 10
@current_stock_array += quantity + 1
else
break
end
end
=> @current_stock_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
####在庫数が5の場合
@current_stock_array = []
@stocks.current_stock.times do |quantity|
if quantity < 10
@current_stock_array += quantity + 1
else
break
end
end
=> @current_stock_array = [1, 2, 3, 4, 5]
####在庫数が0の場合
@current_stock_array = []
@stocks.current_stock.times do |quantity|
if quantity < 10
@current_stock_array += quantity + 1
else
break
end
end
=> @current_stock_array = []
####Viewファイル
= form_for モデルに基づいたインスタンス変数 do |f|
= f.label :quantity, "数量"
= f.select :quantity, @current_stock_array #Controllerで生成した配列を使うようにする
在庫数が0のときは、選択できないセレクトボックスが表示されるが、View側で在庫0のときにセレクトボックスやカートに入れるボタンを表示させないように制御すればOK。
これでなんとか解決。
2019/1/5 追記
Contoller汚したくないので、Helperに書いて呼び出すほうがいいですね。
Decoratorを導入していれば、それに書くほうがいいと思います。
@stock = Stock.find(params[:id])
@cart = Cart.new
= form_for モデルに基づいたインスタンス変数 do |f|
= f.label :quantity, "数量"
= f.select :quantity, stock_array_maker(@stock)
def stock_array_maker(stock)
current_stock_array = [] #配列を生成
stock.current_stock.times do |quantity| #stockの現在の在庫数まで、ループを回す。
if quantity < 10 #quantityが10未満かどうか?
current_stock_array += quantity + 1
#quantityは0からスタートしているので、1足した数を入れる必要がある。
else
break #ループを抜ける
end
end
current_stock_array # 戻り値
end
2019/12/07 それなりに需要があるようなのでさらに追記
改めて読んだらループする必要ないコードでした。。。
= form_for モデルに基づいたインスタンス変数 do |f|
= f.label :quantity, "数量"
= f.select :quantity, stock_array_maker(@stock.current_stock)
def stock_array_maker(current_stock)
current_stock < 10 ? [*1..current_stock] : [*1..10]
end