Reputation: 175
I'm trying to create a Product form that has multiple sizes and prices for each of those sizes.
They way I have it modelled is a has_many :through relationship. The associative table contains an extra field for price such that it will now hold the product_id, size_id, and price.
I'm not sure how I should go about creating my form or how Rails expects this to look. Any help would be much appreciated.
My Product is Cake :)
class Cake < ApplicationRecord
belongs_to :cake_type
has_many :cake_details
has_many :sizes, through: :cake_details
end
Size model
class Size < ApplicationRecord
has_many :cake_details
has_many :cakes, through: :cake_details
end
CakeDetail model class CakeDetail < ApplicationRecord belongs_to :cake belongs_to :size end
my migration
class CreateCakeDetails < ActiveRecord::Migration[5.1]
def change
create_table :cake_details do |t|
t.references :cake, foreign_key: true
t.references :size, foreign_key: true
t.decimal :price, :precision => 10, :scale => 2
t.timestamps
end
end
end
The only thing I'm stuck on is associating the form with the model. i.e. for every size I want to have a text box with price associated with it.
This is currently how I'm approaching it but I have no idea how rails expects the id's of the text box to look or how I should structure this.
This is currently what I'm experimenting with in my form
<%= collection_check_boxes(:cake, :size_ids, Size.all, :id, :name) do |b| %>
<tr>
<td>
<%= b.label %>
</td>
<td>
<%= b.check_box %>
</td>
<td>
<%= form.text_field :cake_detail, id: b.label %>
</td>
</tr>
<% end %>
Upvotes: 0
Views: 543
Reputation: 957
The way you define your business logic is normal
- A product has multiple sizes
- Each size has a price
The only thing I believe that it leads you to the problem is you are trying to create everything at the same time. Even Rails has nested_attributes
which might solve your problem, but let's think once again.
Generally, Size
records are fixed and was created beforehand. So that you don't have to create it at the same time with creating a Product
.
Once you deal with this idea, your problem becomes much easier:
Size
: M, L, XL, XXL ... that was created beforehanddb/seeds.rb
)Product
along ProductDetail
with prices,ProductDetail
with Size
Now you can use Rails's nested_attributes
for the relation Product -> ProductDetail
Your model
# app/models/cake.rb
class Cake < ApplicationRecord
belongs_to :cake_type
has_many :cake_details
has_many :sizes, through: :cake_details
attr_accessor :is_enable
accepts_nested_attributes_for :cake_details, reject_if: :is_not_good_detail?
private
def is_not_good_detail?(attributed)
return true if attributed[:is_enable].to_i != 1
# Check if CakeDetail is good or not
end
end
Your controller
# app/controllers/cakes_controller.rb
class CakesController < ApplicationController
def new
@cake = Cake.new
# Build `cake_details`, so that you can render them at view
Size.each do |size|
@cake.cake_details.build(size_id: size.id, price: 0)
end
end
def create
# Create your Cake + CakeDetail
end
private
def cake_params
# permit your params here
end
end
Your view
# app/views/cakes/_form.html.erb
<%= form_for @cake do |f| %>
<%= f.fields_for :cakes_detail do |field| %>
<%= field.check_box :is_enable %>
<%= field.hidden_field :size_id %>
<%= field.text_field :price %>
<% end>
<% end %>
My code is completely not tested, and you still have a lot of things to do, but it should be the right way to solve your problem, tho.
You can consider the checklist to make it done:
Since the check_box
only produces 0
and 1
value, so using it for size_id
is incorrect. We can solve it by:
attr_accessor
(ex: is_enable
) for CakeDetail
and use it for the check_box
size_id
become a hidden fieldis_enable != 1
You can found here a working example yeuem1vannam/product-size
Upvotes: 0