The setup for this episode because it had a has_many :through association. This is useful to find a record from an instance of the first model via a third model. The way this association was done in this episode was by creating three models, product, category, and categorization. These are the relevant lines of code:
class Product < ActiveRecord::Base has_many :categorizations has_many :categories, through: :categorization end class Category < ActiveRecord::Base has_many :categorizations has_many :products, through: categorization end class Categorization < ActiveRecord::Base belongs_to :product belongs_to :category end
Just like when you have a
belongs_to relationship, you must define the foreign key within the table that has the
belongs_to method. In this case it is the Categorization model. The schema would look like this.
create_table "categorizations", force: true do |t| t.integer 'category_id' t.integer 'product_id' t.datetime 'created_at' t.datetime 'updated_at' end
This setup allows us to call
@product.categories returns an array of category objects associated through the categorization table. This is similar to how you would create followers and followed_users for a user.
The main part of the episode was about how you can create checkboxes to select the categories. First obvious things to do are to loop through all the categories and display them in the
_form partial. The tricky part comes with choosing a
check_box or a
check_box_tag. You want to choose a
check_box_tag so you can display each category, not just one.
Next you want to fill in the value of checked or not checked. The second argument passed into the
check_box_tag is the
category.id. The third is the value of checked or not checked. The way you designate the value is by passing in true or false. The way it is done in the episode is by finding the
@product.category_ids and checking if the
category.id from the block is included within the
Now if you refreshed the page, the value is correctly displayed. A hidden_field_tag is also created in order to make sure that the form is submitted with a default value of nil. The reason you want to include this
category_id field is to make sure that if no checkboxes are checked, then an empty array is passed as a parameter.
Ryan adds a usability enhancement by allowing users to click on the text to check and uncheck boxes. He first creates a unique id for each checkbox with
dom_id. Next he adds a label_tag to the text. Oddly enough the
dom_id method has no description within the Ruby on Rails API. It just has the source code.
dom_id seems to be creating a unique string from the object’s model and id values. In the categories example, the id was defined as
category_1 and so on.
The pertinent code:
<%= hidden_field_tag "product[category_ids]", nil %> <% Category.all.each do |category| %> <%= check_box_tag "product[category_ids]", category.id, @product.category_ids.include?(category.id), id: dom_id(category) %> <%= label_tag dom_id(category), category.name %><br> <% end %>
One gotcha you have to watch out for when following this episode and creating a Rails 4 app is Strong Parameters. I was able to find a post on CoderWall that almost translated one-to-one to what I needed to do. You have to define the
category_id param as an array. The product_params method definition in
def product_params params[:product].permit(:name, :price, category_id: ) end