Topic: Understanding Many to Many

I am new to rails and am struggling to figure out just how to use the many-to-many options in the controller, database and view.

Lets say we have an product model and we have:

has_many :categories

and the categories model:

belongs_to :product

So that in principle creates the relationship in the model. How do I go about adding the links to build this relationship? I am under the impression that you would add something like this to the products controller:

@categories = Category.find(params[:category_id])

And then in the view you could use something like product.categories.XXX.

But I am not sure if this is correct or where to go after the model. I have tried many tutorials and they either old or dont seem to work. Can anyone give me a simple tutorial so I can understand the basic relationship and I can then work from there?

I would be truly grateful.

Thanks

gigg

Re: Understanding Many to Many

You will never get an array of categories like you're trying to do:

@categories = Category.find(params[:category_id])

You'll get one and the only category like that. In your case you can get a product like that:

@category = Category.find(params[:category_id])
#if you need a product you do just like that
@product = @category.product
#but you don't need to do that in the controller because you'll get it anyway in your view

But if you do like that:

@product = Product.find(params[:product_id)
@categories = @product.categories #you get an array of categories belonging to the product

But this is not a many-to-many relation. In many-to-many relation you have in general an intermediate table that either is called CategoriesProducts (by convention it should be named in alphabetical order of two models). In this case in your models you should specify it like that:

Product < AR
    has_and_belongs_to_many :categories
end

Category < AR
  has_and_belongs_to_many :products
end

And in this case you'll be able to get an array of categories for a product and vice versa - an array of products from a category. In this case in your intermediate table CategoriesProducts you'll have to IDs: product_id and category_id.

If you need to process extra values in your intermediate table, in this case you will have to create via migration a separate model, for ex. Inventory as you usually do for any other model. And your models will be like that:

Product < AR
        has_many :inventories
        has_many :categories, :through => :inventories
end


Inventory < AR
belongs_to :product
belongs_to :category

end
Category < AR
   has_many :inventories
   has_many :products, :through => :inventories
end

In your migration you specify product_id, category_id fields as well all other fields you need in your intermediate model. And you can get:

products = category.products #products array for the specified category
categories = products.categories #categories array for the specified product

Last edited by Javix (2010-07-13 08:13:19)

Re: Understanding Many to Many

Hi Javix,

my problem is this:

i have 3 models:

class Hctaff < ActiveRecord::Base
  has_and_belongs_to_many :hctthems
end

class Hctthem < ActiveRecord::Base
  has_and_belongs_to_many :hctaffs
end

class HctaffsHctthems < ActiveRecord::Base
  belongs_to :hctaff
  belongs_to :hctthem
end


i 'd created 3 tables:

hctaff
def self.up
    create_table :hctaffs do |t|
      t.column :titre, :string
      t.timestamps
    end
end

hctthem
def self.up
    create_table :hctthems do |t|
      t.column :titre, :string
      t.timestamps
    end
end

hctaffs_hctthems
def self.up
    create_table :hctaffs_hctthems , :id => false do |t|
      t.column :hctaff_id, :integer
      t.column :hctthem_id, :integer
    end
end

Well, right here, everything work find when i create records with hctaff.

in my search controller, i have:

def search_hctaff
    @search = params[:search].blank? ? {} : params[:search]
    collect_hctaffs
    @hctaffs = @scope.find(:all)
                               
end

def collect_hctaffs 
    @scope = Hctaff.scoped({})
    @scope = @scope.scoped :conditions => ['titre ?', "%#{@search[:titre]}%"] unless @search[:titre].blank?
    @scope = @scope.scoped :conditions => { :hctthem_id => @search[:hctthem_id] } unless @search[:hctthem_id].blank?
end

I would like to create a search view where i can select an hctthem ( select_tag ) and must render all the hctaff with the hctthem id recorded in the join table.
I have to ask and find it in the join table i suppose. but i have no idea how to do it… i'm lost.
Do you have an idea of that ?

Thanks wink