Reputation: 511
I have a Pizza model and Topping model with a PizzaTopping join table. Pizza has many toppings and topping belongs to pizza. I'm a novice with Rails.
My problem is trying to understand how to created a nested form that will add multiple records into my PizzaTopping join table. I also need the toppings to show up in check box form.
<div class="form-horizontal">
<%= form_for(@pizza) do |f| %>
Confusion #1:
To my understand this is to show a full model in check boxes, which works, but I'm confused on how the controller accepts this and creates the records in the join table of the toppings associated with the pizza. I want separate records(and not an attribute that is an array of topping ids):
PizzaTopping.create(id: 1, pizza_id: 1, topping_id: 1)
PizzaTopping.create(id: 2, pizza_id: 1, topping_id: 2)
PizzaTopping.create(id: 3, pizza_id: 1, topping_id: 3)
..
<div class="form-group">
<%= f.collection_check_boxes(:topping_ids, Topping.all, :id, :name) do |b| %>
<%= b.check_box %>
<%= b.label %>
<% end %>
</div>
or Confusion # 2:
This is a nested form but how do I get the toppings in check boxes from the Topping model and same as above, how do I code this in my controller to add records in the join table.
<div class="form-group">
<%= f.fields_for :toppings do |builder| %>
<%= builder.check_box %> // confused what I would even do next
<% end %>
</div>
...
<%= f.submit %>
<% end %>
</div>
Upvotes: 1
Views: 548
Reputation: 102368
First lets setup a indirect many to many relationship though the pizza_toppings
table.
class Pizza < ActiveRecord::Base
has_many :pizza_toppings
has_many :toppings, through: :pizza_toppings
end
class Topping < ActiveRecord::Base
has_many :pizza_toppings
has_many :pizzas, through: :pizza_toppings
end
class PizzaTopping < ActiveRecord::Base
belongs_to :pizza
belongs_to :topping
end
What this accomplishes is that it lets you associate any number of pizzas with any number of toppings and ActiveRecord will handle joining for you:
@pizza = Pizza.find_by(name: 'Vesuvio')
@pizza.toppings
# => Topping( name: cheese ) ...
@pizza.toppings << Topping.find_by(name: 'Ham')
# inserts a record into the pizza_toppings table
# you can also do the inverse
@topping = Topping.find_by(name: 'Anchovies')
@topping.pizzas
# => Pizza( name: 'Napoli' )
To setup one to any or many to many relations via a checkbox you can use the collection_check_boxes
helper.
<% form_for(@pizza) do |f| %>
<% f.collection_check_boxes(:topping_ids, Topping.all, :id, :name) %>
<% end %>
When you give a model a has_many
association it gets a _ids
setter which takes an array of ids and adds / removes associations, in this case ActiveRecord is also smart enough to know that it should setup the association through the join table when you use the through
option.
The checkboxes generated by collection_check_boxes give you just that - an array in the params containing the ids of the selected toppings.
Note that you don't need to use fields_for
here unless you intend to let users create pizzas and toppings on the same page. Also make sure you whitelist the topping_ids
param.
def pizza_params
params.require(:pizza).permit(:name, topping_ids: [])
end
And now you got me all hungry.
Upvotes: 4
Reputation: 1438
First things first, if you are using a join table then you need to organise your relations differently, it makes no sense to use a join table with a belongs_to relationship, what you want to do is organise your relations so that a Pizza has_many :toppings, and a Topping has_many :pizzas and use the through: :pizza_toppings key.
Now onto your first confusion,
If you are using checkboxes the most you can hope to receive is the value of that checkbox, most likely the ids, this is then down to you to instantiate the records once you have the array of ids, perhaps something along the lines of selected_toppings = Topping.where(id: params[:topping_ids])
which will instantiate a collection of the toppings that were selected.
Also if you have your relations set up properly there is no need to explicitly create PizzaTopping records, this is a join table, and in my opinion I prefer to handle everything using the relations, something along the lines of pizza.toppings << selected_toppings
should do the trick to set it all up.
Confusion two: I dont think what you are looking for is a nested form, because you are not actually creating toppings, you just want a list of the toppings to relate to a pizza. Nested forms are when you want to create/edit and then write the attributes of a relation, your toppings are already preset, so just simply make checkboxes in the form normally with the values of your toppings ids.
Hope this helps!
Upvotes: 0