Search code examples
jqueryruby-on-railsrubyhas-many-throughself-reference

RubyOnRails self reference many to many


I've got a Product model and I need to implement related products, so I thought a self reference is the best way to do it:

class Product < ActiveRecord::Base
  has_many :related_product_associations, class_name: "RelatedProduct"
  has_many :related_products, through: :related_product_associations, source: :related_product
end

class RelatedProduct < ActiveRecord::Base
    belongs_to :product
    belongs_to :related_product, class_name: "Product"
end

In products_controller:

params.require(:product).permit(..., :related_products, ...)

Questions: 1. Are my models/controller all right 2. How to make form for several related products.

I thought the forms may look something like this:

<%= f.collection_select :related_product, @products, :id, :name, include_blank: true %>
<%= f.collection_select :related_product, @products, :id, :name, include_blank: true %>
<%= f.collection_select :related_product, @products, :id, :name, include_blank: true %>

In products_controller:

@products = Product.all
  1. Is there a way to implement cocoon-like behavior with jQuery nested forms so I can add more fields only if I need them?

Solution

  • Your goal is to submit an array of ids in this form related_product_ids=[1,2,3,4].

    To do that, the param has to be named product[realted_product_ids][]. Unfortunately, you can't do this. The second param has to be a param

    # Note this doesn't work
    <%= f.collection_select 'related_product_ids[]', @products, :id, :name, include_blank: true %> 
    

    But, you can do this (with multiple)

    <%= f.collection_select :related_product_ids, @products, :id, :name, {:selected => @product.related_product_ids, :include_blank => true}, {:multiple => true} %>
    

    Or you can do this

    <%- (1..4).each do |i| %>
      <%= select_tag 'product[related_product_ids][]', options_for_select(@products.map{|p| [p.name, p.id]}, @product.related_product_ids[i]), :include_blank => true %>
    <% end %>
    

    You can also try it out with check boxes. Later, switching to dropdowns added dynamically shouldn't be too hard.

    <%- @products.each do |product| %>
      <%= check_box_tag 'product[related_product_ids][]', product.id, @product.related_product_ids.include?(product.id) %>
      <%= product.name %>
    <%- end %>