Spasm
Spasm

Reputation: 805

has_many :through Rails 3.1.1

Updated the question as original was over simplistic, thanks to Aaron McLeod aka AGMCLEOD for your help highlighting this


My problem is such that it is not that I cannot get it to work, but getting it to work how I want.

The problem

Imagine 3 models, a Company, Branch and a Contact, they are linked together through a CompanyContacts model or a look-up table.

The table company_contacts looks like this:

+------------+-----------+------------+
| company_id | branch_id | contact_id |
+------------+-----------+------------+

and my ideal statements for relating a contact to a branch and company should be:

update company_contacts set contact_id = x where branch_id = y AND company_id = z

So the question, How is this possible using ActiveRecord? Do you create custom methods to set it in one go?

What I have in my models

CompanyContact Model

belongs_to :company, :class_name => "Company", :foreign_key => :company_id
belongs_to :branch,  :class_name => "Branch",  :foreign_key => :branch_id
belongs_to :contact, :class_name => "Contact", :foreign_key => :contact_id

Company Model

has_many :company_contacts
has_many :branches,  :through => :company_contacts, :source => :branches
has_many :contacts,  :through => :company_contacts, :source => :contacts 

Branch Model

has_many :company_contacts
has_many :companies, :through => :company_contacts, :source => :companies
has_many :contacts,  :through => :company_contacts, :source => :contacts 

I would love to be able to do the following but it is not possible

has_one :company, :through => :company_contacts, :source => :company

Of course I could simulate it by

def branch
  Branch.companies.first
end

Contact Model

has_many :company_contacts
has_many :branches, :through => :company_contacts, :source => :branches
has_many :companies, :through => :company_contacts, :source => :companies

Again ideally i would like to have, has_one branch and has_one company

My current solution when creating a contact and << to a branch results in two rows being created

+------------+-----------+------------+
| company_id | branch_id | contact_id |
+------------+-----------+------------+
| 1          | 1         | nil        |
+------------+-----------+------------+
| nil        | 1         | 1          |
+------------+-----------+------------+

When, what in fact I would like to achieve is:

+------------+-----------+------------+
| company_id | branch_id | contact_id |
+------------+-----------+------------+
| 1          | 1         | 1          |
+------------+-----------+------------+

The only way I can think of doing this is creating a custom method

I will continue to try and resolve my issue and post my findings Look forward to your replies

Thanks in advance.

Upvotes: 0

Views: 956

Answers (2)

agmcleod
agmcleod

Reputation: 13611

To solve this, you can do something like the following. Assuming you're passing the branch id and company id through the form, or if you have it already in the url.

def create
  @contact = Contact.new(params[:contact])
  if @contact.save
    cc = CompanyContact.new(:contact_id => @contact.id, :branch_id => params[:branch_id], :company_id => params[:company_id])
    cc.save
    redirect_to contacts_url, :notice => "Contact saved"
  else
    render :action => "new"
  end
end

You might need to adjust parameter names and such, but that should work. Because it is a bit of three way connection, I'm not sure if there's a way to build it right on the contact object.

Upvotes: 0

Spasm
Spasm

Reputation: 805

ok after a hell of a lot more digging around after posting the question I came across this awesome answer to something similar. zetetic provided a very detailed answer and I read up in a little more depth on how to resolve this issue.

First the inspiration: has_many :through multiple has_one relationships?

Now the answer:

In the create method in your ContactController the magic 1 line of code is:

@contact.company_contacts.build(:company_id => @company.id, :branch_id => @branch.id)

So in full in ContactController's create method:

# fetch the branch and company ids first

@contact = Contact.new(params[:contact])
@contact.company_contacts.build(:company_id => @company.id, :branch_id => @branch.id)
@contact.save

and it does exactly what I wanted to achieve with:

INSERT INTO "company_contacts" ("branch_id", "company_id", "contact_id") VALUES (?, ?, ?)  [["branch_id", 2], ["company_id", 4], ["contact_id", 12]]

Phew - took me a while for some reason and seems so simple looking at it!

Upvotes: 1

Related Questions